Index: pkg/kernel/lib/verifier.dart |
diff --git a/pkg/kernel/lib/verifier.dart b/pkg/kernel/lib/verifier.dart |
index c518a6566f3f562eeb19d198f6e568c89256dc47..18b93c320b958c61207f85c805d3a741a2582b03 100644 |
--- a/pkg/kernel/lib/verifier.dart |
+++ b/pkg/kernel/lib/verifier.dart |
@@ -38,13 +38,17 @@ class VerificationError { |
} |
} |
+enum TypedefState { Done, BeingChecked } |
+ |
/// Checks that a kernel program is well-formed. |
/// |
/// This does not include any kind of type checking. |
class VerifyingVisitor extends RecursiveVisitor { |
final Set<Class> classes = new Set<Class>(); |
- final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); |
+ final Set<Typedef> typedefs = new Set<Typedef>(); |
+ Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>(); |
final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; |
+ final Map<Typedef, TypedefState> typedefState = <Typedef, TypedefState>{}; |
bool classTypeParametersAreInScope = false; |
/// If true, relax certain checks for *outline* mode. For example, don't |
@@ -67,7 +71,8 @@ class VerifyingVisitor extends RecursiveVisitor { |
visitChildren(node); |
} |
- problem(TreeNode node, String details) { |
+ problem(TreeNode node, String details, {TreeNode context}) { |
+ context ??= this.context; |
throw new VerificationError(context, node, details); |
} |
@@ -75,7 +80,8 @@ class VerifyingVisitor extends RecursiveVisitor { |
if (!identical(node.parent, currentParent)) { |
problem( |
node, |
- "Incorrect parent pointer: expected '${node.parent.runtimeType}'," |
+ "Incorrect parent pointer on ${node.runtimeType}:" |
+ " expected '${node.parent.runtimeType}'," |
" but found: '${currentParent.runtimeType}'."); |
} |
var oldParent = currentParent; |
@@ -135,14 +141,14 @@ class VerifyingVisitor extends RecursiveVisitor { |
void declareTypeParameters(List<TypeParameter> parameters) { |
for (int i = 0; i < parameters.length; ++i) { |
var parameter = parameters[i]; |
- if (!typeParameters.add(parameter)) { |
+ if (!typeParametersInScope.add(parameter)) { |
problem(parameter, "Type parameter '$parameter' redeclared."); |
} |
} |
} |
void undeclareTypeParameters(List<TypeParameter> parameters) { |
- typeParameters.removeAll(parameters); |
+ typeParametersInScope.removeAll(parameters); |
} |
void checkVariableInScope(VariableDeclaration variable, TreeNode where) { |
@@ -159,6 +165,11 @@ class VerifyingVisitor extends RecursiveVisitor { |
problem(class_, "Class '$class_' declared more than once."); |
} |
} |
+ for (var typedef_ in library.typedefs) { |
+ if (!typedefs.add(typedef_)) { |
+ problem(typedef_, "Typedef '$typedef_' declared more than once."); |
+ } |
+ } |
library.members.forEach(declareMember); |
for (var class_ in library.classes) { |
class_.members.forEach(declareMember); |
@@ -176,6 +187,32 @@ class VerifyingVisitor extends RecursiveVisitor { |
} |
} |
+ void checkTypedef(Typedef node) { |
+ var state = typedefState[node]; |
+ if (state == TypedefState.Done) return; |
+ if (state == TypedefState.BeingChecked) { |
+ problem(node, "The typedef '$node' refers to itself", context: node); |
+ } |
+ assert(state == null); |
+ typedefState[node] = TypedefState.BeingChecked; |
+ var savedTypeParameters = typeParametersInScope; |
+ typeParametersInScope = node.typeParameters.toSet(); |
+ var savedParent = currentParent; |
+ currentParent = node; |
+ // Visit children without checking the parent pointer on the typedef itself |
+ // since this can be called from a context other than its true parent. |
+ node.visitChildren(this); |
+ currentParent = savedParent; |
+ typeParametersInScope = savedTypeParameters; |
+ typedefState[node] = TypedefState.Done; |
+ } |
+ |
+ visitTypedef(Typedef node) { |
+ checkTypedef(node); |
+ // Enter and exit the node to check the parent pointer on the typedef node. |
+ exitParent(enterParent(node)); |
+ } |
+ |
visitField(Field node) { |
currentMember = node; |
var oldParent = enterParent(node); |
@@ -184,6 +221,7 @@ class VerifyingVisitor extends RecursiveVisitor { |
classTypeParametersAreInScope = false; |
visitList(node.annotations, this); |
exitParent(oldParent); |
+ node.type.accept(this); |
currentMember = null; |
} |
@@ -492,9 +530,17 @@ class VerifyingVisitor extends RecursiveVisitor { |
} |
@override |
+ visitTypedefReference(Typedef node) { |
+ if (!typedefs.contains(node)) { |
+ problem( |
+ node, "Dangling reference to '$node', parent is: '${node.parent}'"); |
+ } |
+ } |
+ |
+ @override |
visitTypeParameterType(TypeParameterType node) { |
var parameter = node.parameter; |
- if (!typeParameters.contains(parameter)) { |
+ if (!typeParametersInScope.contains(parameter)) { |
problem( |
currentParent, |
"Type parameter '$parameter' referenced out of" |
@@ -519,6 +565,19 @@ class VerifyingVisitor extends RecursiveVisitor { |
" ${node.classNode.typeParameters.length} parameters."); |
} |
} |
+ |
+ @override |
+ visitTypedefType(TypedefType node) { |
+ checkTypedef(node.typedefNode); |
+ node.visitChildren(this); |
+ if (node.typeArguments.length != node.typedefNode.typeParameters.length) { |
+ problem( |
+ currentParent, |
+ "The typedef type $node provides ${node.typeArguments.length}" |
+ " type arguments but the typedef declares" |
+ " ${node.typedefNode.typeParameters.length} parameters."); |
+ } |
+ } |
} |
class CheckParentPointers extends Visitor { |