| 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 {
|
|
|