Implement latest rules prohibiting some self referencing typedefs.

Update tests.
Review URL: https://chromiumcodereview.appspot.com//10377095

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7524 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
regis@google.com 2012-05-10 23:03:36 +00:00
parent dec5e465f9
commit 5d9dab620c
9 changed files with 169 additions and 59 deletions

View file

@ -563,9 +563,8 @@ void ClassFinalizer::FinalizeTypeArguments(
FinalizationKind finalization) {
ASSERT(arguments.Length() >= cls.NumTypeArguments());
if (!cls.is_finalized()) {
const GrowableObjectArray& visited =
GrowableObjectArray::Handle(GrowableObjectArray::New());
ResolveInterfaces(cls, visited);
GrowableArray<intptr_t> visited_interfaces;
ResolveInterfaces(cls, &visited_interfaces);
FinalizeTypeParameters(cls);
}
Type& super_type = Type::Handle(cls.super_type());
@ -669,9 +668,8 @@ RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls,
// parameters of the type class must be finalized.
Class& type_class = Class::Handle(parameterized_type.type_class());
if (!type_class.is_finalized()) {
const GrowableObjectArray& visited =
GrowableObjectArray::Handle(GrowableObjectArray::New());
ResolveInterfaces(type_class, visited);
GrowableArray<intptr_t> visited_interfaces;
ResolveInterfaces(type_class, &visited_interfaces);
FinalizeTypeParameters(type_class);
}
@ -752,10 +750,8 @@ RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls,
}
}
// Illegally self referencing types may get finalized indirectly.
if (parameterized_type.IsFinalized()) {
ASSERT(parameterized_type.IsMalformed());
} else {
// Self referencing types may get finalized indirectly.
if (!parameterized_type.IsFinalized()) {
// Mark the type as finalized.
if (parameterized_type.IsInstantiated()) {
parameterized_type.set_is_finalized_instantiated();
@ -791,18 +787,30 @@ RawAbstractType* ClassFinalizer::FinalizeType(const Class& cls,
}
}
// If the type class is a signature class, we also finalize its signature
// type, thereby finalizing the result type and parameter types of its
// signature function.
// If the type class is a signature class, we are currently finalizing a
// signature type, i.e. finalizing the result type and parameter types of the
// signature function of this signature type.
// We do this after marking this type as finalized in order to allow a
// function type to refer to itself via its parameter types and result type.
if (type_class.IsSignatureClass()) {
// Signature classes are finalized upon creation.
ASSERT(type_class.is_finalized());
// Resolve and finalize the result and parameter types of the signature
// function of this signature class.
ResolveAndFinalizeSignature(
type_class, Function::Handle(type_class.signature_function()));
// Signature classes are finalized upon creation, except function type
// aliases.
if (type_class.IsCanonicalSignatureClass()) {
ASSERT(type_class.is_finalized());
// Resolve and finalize the result and parameter types of the signature
// function of this signature class.
ASSERT(type_class.SignatureType() == type.raw());
ResolveAndFinalizeSignature(
type_class, Function::Handle(type_class.signature_function()));
} else {
// This type is a function type alias. Its class may need to be finalized
// and checked for illegal self reference.
FinalizeClass(type_class, false);
// Finalizing the signature function here (as in the canonical case above)
// would not mark the canonical signature type as finalized.
const Type& signature_type = Type::Handle(type_class.SignatureType());
FinalizeType(cls, signature_type, finalization);
}
}
return parameterized_type.Canonicalize();
@ -1059,8 +1067,6 @@ void ClassFinalizer::FinalizeClass(const Class& cls, bool generating_snapshot) {
if (FLAG_trace_class_finalization) {
OS::Print("Finalize %s\n", cls.ToCString());
}
// Signature classes are finalized upon creation.
ASSERT(!cls.IsSignatureClass());
if (!IsSuperCycleFree(cls)) {
const String& name = String::Handle(cls.Name());
const Script& script = Script::Handle(cls.script());
@ -1068,9 +1074,8 @@ void ClassFinalizer::FinalizeClass(const Class& cls, bool generating_snapshot) {
"class '%s' has a cycle in its superclass relationship",
name.ToCString());
}
const GrowableObjectArray& visited =
GrowableObjectArray::Handle(GrowableObjectArray::New());
ResolveInterfaces(cls, visited);
GrowableArray<intptr_t> visited_interfaces;
ResolveInterfaces(cls, &visited_interfaces);
// Finalize super class.
const Class& super_class = Class::Handle(cls.SuperClass());
if (!super_class.IsNull()) {
@ -1084,6 +1089,24 @@ void ClassFinalizer::FinalizeClass(const Class& cls, bool generating_snapshot) {
super_type ^= FinalizeType(cls, super_type, kFinalizeWellFormed);
cls.set_super_type(super_type);
}
// Signature classes are finalized upon creation, except function type
// aliases.
if (cls.IsSignatureClass()) {
ASSERT(!cls.IsCanonicalSignatureClass());
// Check for illegal self references.
GrowableArray<intptr_t> visited_aliases;
if (!IsAliasCycleFree(cls, &visited_aliases)) {
const String& name = String::Handle(cls.Name());
const Script& script = Script::Handle(cls.script());
ReportError(script, cls.token_index(),
"typedef '%s' illegally refers to itself",
name.ToCString());
}
// TODO(regis): Also check this: "It is a compile-time error if any default
// values are specified in the signature of a function type alias".
cls.Finalize();
return;
}
// Finalize factory class, if any.
if (cls.is_interface()) {
if (cls.HasFactoryClass()) {
@ -1153,6 +1176,58 @@ bool ClassFinalizer::IsSuperCycleFree(const Class& cls) {
}
// Returns false if the function type alias illegally refers to itself.
bool ClassFinalizer::IsAliasCycleFree(const Class& cls,
GrowableArray<intptr_t>* visited) {
ASSERT(cls.IsSignatureClass());
ASSERT(!cls.IsCanonicalSignatureClass());
ASSERT(!cls.is_finalized());
ASSERT(visited != NULL);
const intptr_t cls_index = cls.index();
for (int i = 0; i < visited->length(); i++) {
if ((*visited)[i] == cls_index) {
// We have already visited alias 'cls'. We found a cycle.
return false;
}
}
// Visit the result type and parameter types of this signature type.
visited->Add(cls.index());
const Function& function = Function::Handle(cls.signature_function());
// Check class of result type.
AbstractType& type = AbstractType::Handle(function.result_type());
ResolveType(cls, type, kFinalize);
if (type.IsType() && !type.IsMalformed()) {
const Class& type_class = Class::Handle(type.type_class());
if (!type_class.is_finalized() &&
type_class.IsSignatureClass() &&
!type_class.IsCanonicalSignatureClass()) {
if (!IsAliasCycleFree(type_class, visited)) {
return false;
}
}
}
// Check classes of formal parameter types.
const intptr_t num_parameters = function.NumberOfParameters();
for (intptr_t i = 0; i < num_parameters; i++) {
type = function.ParameterTypeAt(i);
ResolveType(cls, type, kFinalize);
if (type.IsType() && !type.IsMalformed()) {
const Class& type_class = Class::Handle(type.type_class());
if (!type_class.is_finalized() &&
type_class.IsSignatureClass() &&
!type_class.IsCanonicalSignatureClass()) {
if (!IsAliasCycleFree(type_class, visited)) {
return false;
}
}
}
}
visited->RemoveLast();
return true;
}
bool ClassFinalizer::AddInterfaceIfUnique(
const GrowableObjectArray& interface_list,
const AbstractType& interface,
@ -1191,12 +1266,11 @@ bool ClassFinalizer::AddInterfaceIfUnique(
// graph. If we visit an interface a second time on a given path,
// we found a loop.
void ClassFinalizer::ResolveInterfaces(const Class& cls,
const GrowableObjectArray& visited) {
ASSERT(!visited.IsNull());
Class& visited_cls = Class::Handle();
for (int i = 0; i < visited.Length(); i++) {
visited_cls ^= visited.At(i);
if (visited_cls.raw() == cls.raw()) {
GrowableArray<intptr_t>* visited) {
ASSERT(visited != NULL);
const intptr_t cls_index = cls.index();
for (int i = 0; i < visited->length(); i++) {
if ((*visited)[i] == cls_index) {
// We have already visited interface class 'cls'. We found a cycle.
const String& interface_name = String::Handle(cls.Name());
const Script& script = Script::Handle(cls.script());
@ -1219,7 +1293,7 @@ void ClassFinalizer::ResolveInterfaces(const Class& cls,
(cls.library() == Library::CoreImplLibrary());
// Resolve and check the interfaces of cls.
visited.Add(cls);
visited->Add(cls_index);
AbstractType& interface = AbstractType::Handle();
Class& interface_class = Class::Handle();
for (intptr_t i = 0; i < super_interfaces.Length(); i++) {
@ -1260,7 +1334,7 @@ void ClassFinalizer::ResolveInterfaces(const Class& cls,
// Now resolve the super interfaces.
ResolveInterfaces(interface_class, visited);
}
visited.RemoveLast();
visited->RemoveLast();
}

View file

@ -90,13 +90,15 @@ class ClassFinalizer : public AllStatic {
static bool FinalizePendingClasses(bool generating_snapshot);
static void FinalizeClass(const Class& cls, bool generating_snapshot);
static bool IsSuperCycleFree(const Class& cls);
static bool IsAliasCycleFree(const Class& cls,
GrowableArray<intptr_t>* visited);
static void CheckForLegalConstClass(const Class& cls);
static RawClass* ResolveClass(const Class& cls,
const UnresolvedClass& unresolved_class);
static void ResolveSuperType(const Class& cls);
static void ResolveFactoryClass(const Class& cls);
static void ResolveInterfaces(const Class& cls,
const GrowableObjectArray& visited);
GrowableArray<intptr_t>* visited);
static void FinalizeTypeParameters(const Class& cls);
static void FinalizeTypeArguments(const Class& cls,
const AbstractTypeArguments& arguments,

View file

@ -606,7 +606,7 @@ RawError* Object::Init(Isolate* isolate) {
object_store->set_object_class(cls);
cls.set_name(String::Handle(String::NewSymbol("Object")));
cls.set_script(script);
cls.set_class_state(RawClass::kPreFinalized);
cls.set_is_prefinalized();
core_lib.AddClass(cls);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
@ -998,6 +998,14 @@ RawString* Class::Name() const {
RawType* Class::SignatureType() const {
ASSERT(IsSignatureClass());
const Function& function = Function::Handle(signature_function());
ASSERT(!function.IsNull());
if (function.signature_class() != raw()) {
// This class is a function type alias. Return the canonical signature type.
const Class& canonical_class = Class::Handle(function.signature_class());
return canonical_class.SignatureType();
}
// Return the first canonical signature type if already computed.
const Array& signature_types = Array::Handle(canonical_types());
if (signature_types.Length() > 0) {
@ -1007,7 +1015,6 @@ RawType* Class::SignatureType() const {
return signature_type.raw();
}
}
ASSERT(IsSignatureClass());
// A signature class extends class Instance and is parameterized in the same
// way as the owner class of its non-static signature function.
// It is not type parameterized if its signature function is static.
@ -1463,8 +1470,14 @@ RawClass* Class::NewSignatureClass(const String& name,
// the signature class.
if (signature_function.signature_class() == Object::null()) {
signature_function.set_signature_class(result);
result.set_is_finalized();
} else {
// This new signature class is an alias.
ASSERT(!result.IsCanonicalSignatureClass());
// Do not yet mark it as finalized, so that the class finalizer can check it
// for illegal self references.
result.set_is_prefinalized();
}
result.set_is_finalized();
// Instances of a signature class can only be closures.
ASSERT(result.instance_size() == Closure::InstanceSize());
// Cache the signature type as the first canonicalized type in result.
@ -1657,6 +1670,12 @@ void Class::set_is_finalized() const {
}
void Class::set_is_prefinalized() const {
ASSERT(!is_finalized());
set_class_state(RawClass::kPreFinalized);
}
void Class::set_interfaces(const Array& value) const {
// Verification and resolving of interfaces occurs in finalizer.
ASSERT(!value.IsNull());

View file

@ -655,6 +655,8 @@ class Class : public Object {
return raw_ptr()->class_state_ == RawClass::kPreFinalized;
}
void set_is_prefinalized() const;
bool is_const() const {
return raw_ptr()->is_const_;
}

View file

@ -2962,6 +2962,10 @@ void Parser::ParseFunctionTypeAlias(
Class::NewSignatureClass(*alias_name,
signature_function,
script_));
// This alias should not be marked as finalized yet, since it needs to be
// checked in the class finalizer for illegal self references.
ASSERT(!function_type_alias.IsCanonicalSignatureClass());
ASSERT(!function_type_alias.is_finalized());
library_.AddClass(function_type_alias);
ExpectSemicolon();
pending_classes.Add(function_type_alias, Heap::kOld);

View file

@ -1,10 +1,14 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Dart test for self referencing function type alias.
// Dart test for illegally self referencing function type alias.
typedef Handle Handle(String command);
typedef Handle Handle(String command); /// 00: compile-time error
typedef F(F x); /// 01: compile-time error
typedef A(B x); /// 02: compile-time error
typedef B(A x); /// 02: continued
main() {
Handle handle = null;
}

View file

@ -1,11 +1,23 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Dart test for self referencing function type alias.
// Dart test for legally self referencing function type alias.
typedef F(F x);
typedef F(List<F> x);
typedef D C();
class D {
C foo() { }
D bar() { }
}
main() {
var func = _(F x) { };
Expect.isTrue(func is F);
var f = _(List x) { };
Expect.isTrue(f is F);
var g = _(List<F> x) { };
Expect.isTrue(g is F);
var d = new D();
Expect.isTrue(d.foo is !C);
Expect.isTrue(d.bar is C);
}

View file

@ -1,14 +0,0 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Dart test for self referencing function type alias.
typedef A(B x);
typedef B(A x);
main() {
var aFunc = _(B x) { };
var bFunc = _(A x) { };
Expect.isTrue(aFunc is A);
Expect.isTrue(bFunc is B);
}

View file

@ -89,6 +89,9 @@ duplicate_implements_test: Skip # Issue 976
field3a_negative_test: Fail
field_method4_negative_test: Fail
final_syntax_test/*: Skip # can't handle base case
function_type_alias5_test/00: Fail # Legally self referencing typedef
function_type_alias5_test/01: Fail # Legally self referencing typedef
function_type_alias5_test/02: Fail # Legally self referencing typedef
function_type_alias_negative_test: Fail # Bug 5231617.
generic_parameterized_extends_test: Skip # Bug 5392297
getters_setters_type3_test: Fail # Issue 2351
@ -275,8 +278,10 @@ function_type_alias_test: Fail
function_type_alias2_test: Fail
function_type_alias3_test: Fail
function_type_alias4_test: Fail
function_type_alias5_test/00: Fail
function_type_alias5_test/01: Fail
function_type_alias5_test/02: Fail
function_type_alias6_test: Fail
function_type_alias7_test: Fail
function_type_parameter2_test: Fail
function_type_parameter_test: Fail
generic_deep_test: Fail
@ -492,8 +497,10 @@ function_test: Fail # internal error: Closures inside initializers not implement
function_type_alias2_test: Fail # cannot resolve type f1
function_type_alias3_test: Fail # cannot resolve type F
function_type_alias4_test: Fail # cannot resolve type F
function_type_alias5_test/00: Fail # visitIs for typedefs not implemented
function_type_alias5_test/01: Fail # visitIs for typedefs not implemented
function_type_alias5_test/02: Fail # visitIs for typedefs not implemented
function_type_alias6_test: Fail # visitIs for typedefs not implemented
function_type_alias7_test: Fail # visitIs for typedefs not implemented
function_type_alias_test: Fail # cannot resolve type Fun
function_type_parameter2_test: Fail # Internal Error: expected optional parameters
function_type_parameter_test: Fail # Internal Error: expected optional parameters