Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Unified Diff: pkg/analyzer/lib/src/generated/constant.dart

Issue 1167413005: In constant evaluation, consistently handle const depending on non-const. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/all_the_rest_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/lib/src/generated/constant.dart
diff --git a/pkg/analyzer/lib/src/generated/constant.dart b/pkg/analyzer/lib/src/generated/constant.dart
index ddf4a3a3b0243c7ece15a3bb10d07d4816b1f07c..9cf3a6cc1fd2003759b35ff8f137e0d608bfd392 100644
--- a/pkg/analyzer/lib/src/generated/constant.dart
+++ b/pkg/analyzer/lib/src/generated/constant.dart
@@ -329,33 +329,38 @@ class ConstantEvaluationEngine {
}
}
} else if (constant is VariableElement) {
- RecordingErrorListener errorListener = new RecordingErrorListener();
- ErrorReporter errorReporter =
- new ErrorReporter(errorListener, constant.source);
- DartObjectImpl dartObject =
- (constant as PotentiallyConstVariableElement).constantInitializer
- .accept(new ConstantVisitor(this, errorReporter));
- // Only check the type for truly const declarations (don't check final
- // fields with initializers, since their types may be generic. The type
- // of the final field will be checked later, when the constructor is
- // invoked).
- if (dartObject != null && constant.isConst) {
- if (!runtimeTypeMatch(dartObject, constant.type)) {
- errorReporter.reportErrorForElement(
- CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, constant,
- [dartObject.type, constant.type]);
+ Expression constantInitializer =
+ (constant as PotentiallyConstVariableElement).constantInitializer;
+ if (constantInitializer != null) {
+ RecordingErrorListener errorListener = new RecordingErrorListener();
+ ErrorReporter errorReporter =
+ new ErrorReporter(errorListener, constant.source);
+ DartObjectImpl dartObject = constantInitializer
+ .accept(new ConstantVisitor(this, errorReporter));
+ // Only check the type for truly const declarations (don't check final
+ // fields with initializers, since their types may be generic. The type
+ // of the final field will be checked later, when the constructor is
+ // invoked).
+ if (dartObject != null && constant.isConst) {
+ if (!runtimeTypeMatch(dartObject, constant.type)) {
+ errorReporter.reportErrorForElement(
+ CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH,
+ constant, [dartObject.type, constant.type]);
+ }
}
+ (constant as VariableElementImpl).evaluationResult =
+ new EvaluationResultImpl(dartObject, errorListener.errors);
}
- (constant as VariableElementImpl).evaluationResult =
- new EvaluationResultImpl(dartObject, errorListener.errors);
} else if (constant is ConstructorElement) {
- // No evaluation needs to be done; constructor declarations are only in
- // the dependency graph to ensure that any constants referred to in
- // initializer lists and parameter defaults are evaluated before
- // invocations of the constructor. However we do need to annotate the
- // element as being free of constant evaluation cycles so that later code
- // will know that it is safe to evaluate.
- (constant as ConstructorElementImpl).isCycleFree = true;
+ if (constant.isConst) {
+ // No evaluation needs to be done; constructor declarations are only in
+ // the dependency graph to ensure that any constants referred to in
+ // initializer lists and parameter defaults are evaluated before
+ // invocations of the constructor. However we do need to annotate the
+ // element as being free of constant evaluation cycles so that later
+ // code will know that it is safe to evaluate.
+ (constant as ConstructorElementImpl).isCycleFree = true;
+ }
} else if (constant is ConstantEvaluationTarget_Annotation) {
Annotation constNode = constant.annotation;
ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation;
@@ -369,7 +374,15 @@ class ConstantEvaluationEngine {
// Just copy the evaluation result.
VariableElementImpl variableElement =
element.variable as VariableElementImpl;
- elementAnnotation.evaluationResult = variableElement.evaluationResult;
+ if (variableElement.evaluationResult != null) {
+ elementAnnotation.evaluationResult =
+ variableElement.evaluationResult;
+ } else {
+ // This could happen in the event that the annotation refers to a
+ // non-constant. The error is detected elsewhere, so just silently
+ // ignore it here.
+ elementAnnotation.evaluationResult = new EvaluationResultImpl(null);
+ }
} else if (element is ConstructorElementImpl &&
element.isConst &&
constNode.arguments != null) {
@@ -405,6 +418,13 @@ class ConstantEvaluationEngine {
* Determine which constant elements need to have their values computed
* prior to computing the value of [constant], and report them using
* [callback].
+ *
+ * Note that it's possible (in erroneous code) for a constant to depend on a
+ * non-constant. When this happens, we report the dependency anyhow so that
+ * if the non-constant changes to a constant, we will know to recompute the
+ * thing that depends on it. [computeDependencies] and
+ * [computeConstantValue] are responsible for ignoring the request if they
+ * are asked to act on a non-constant target.
*/
void computeDependencies(
ConstantEvaluationTarget constant, ReferenceFinderCallback callback) {
@@ -423,56 +443,60 @@ class ConstantEvaluationEngine {
initializer.accept(referenceFinder);
}
} else if (constant is ConstructorElementImpl) {
- constant.isCycleFree = false;
- ConstructorElement redirectedConstructor =
- getConstRedirectedConstructor(constant);
- if (redirectedConstructor != null) {
- ConstructorElement redirectedConstructorBase =
- ConstantEvaluationEngine._getConstructorBase(redirectedConstructor);
- callback(redirectedConstructorBase);
- return;
- } else if (constant.isFactory) {
- // Factory constructor, but getConstRedirectedConstructor returned
- // null. This can happen if we're visiting one of the special external
- // const factory constructors in the SDK, or if the code contains
- // errors (such as delegating to a non-const constructor, or delegating
- // to a constructor that can't be resolved). In any of these cases,
- // we'll evaluate calls to this constructor without having to refer to
- // any other constants. So we don't need to report any dependencies.
- return;
- }
- bool superInvocationFound = false;
- List<ConstructorInitializer> initializers = constant.constantInitializers;
- for (ConstructorInitializer initializer in initializers) {
- if (initializer is SuperConstructorInvocation) {
- superInvocationFound = true;
+ if (constant.isConst) {
+ constant.isCycleFree = false;
+ ConstructorElement redirectedConstructor =
+ getConstRedirectedConstructor(constant);
+ if (redirectedConstructor != null) {
+ ConstructorElement redirectedConstructorBase =
+ ConstantEvaluationEngine
+ ._getConstructorBase(redirectedConstructor);
+ callback(redirectedConstructorBase);
+ return;
+ } else if (constant.isFactory) {
+ // Factory constructor, but getConstRedirectedConstructor returned
+ // null. This can happen if we're visiting one of the special external
+ // const factory constructors in the SDK, or if the code contains
+ // errors (such as delegating to a non-const constructor, or delegating
+ // to a constructor that can't be resolved). In any of these cases,
+ // we'll evaluate calls to this constructor without having to refer to
+ // any other constants. So we don't need to report any dependencies.
+ return;
}
- initializer.accept(referenceFinder);
- }
- if (!superInvocationFound) {
- // No explicit superconstructor invocation found, so we need to
- // manually insert a reference to the implicit superconstructor.
- InterfaceType superclass =
- (constant.returnType as InterfaceType).superclass;
- if (superclass != null && !superclass.isObject) {
- ConstructorElement unnamedConstructor = ConstantEvaluationEngine
- ._getConstructorBase(superclass.element.unnamedConstructor);
- if (unnamedConstructor != null && unnamedConstructor.isConst) {
- callback(unnamedConstructor);
+ bool superInvocationFound = false;
+ List<ConstructorInitializer> initializers =
+ constant.constantInitializers;
+ for (ConstructorInitializer initializer in initializers) {
+ if (initializer is SuperConstructorInvocation) {
+ superInvocationFound = true;
}
+ initializer.accept(referenceFinder);
}
- }
- for (FieldElement field in constant.enclosingElement.fields) {
- // Note: non-static const isn't allowed but we handle it anyway so that
- // we won't be confused by incorrect code.
- if ((field.isFinal || field.isConst) &&
- !field.isStatic &&
- field.initializer != null) {
- callback(field);
+ if (!superInvocationFound) {
+ // No explicit superconstructor invocation found, so we need to
+ // manually insert a reference to the implicit superconstructor.
+ InterfaceType superclass =
+ (constant.returnType as InterfaceType).superclass;
+ if (superclass != null && !superclass.isObject) {
+ ConstructorElement unnamedConstructor = ConstantEvaluationEngine
+ ._getConstructorBase(superclass.element.unnamedConstructor);
+ if (unnamedConstructor != null) {
+ callback(unnamedConstructor);
+ }
+ }
+ }
+ for (FieldElement field in constant.enclosingElement.fields) {
+ // Note: non-static const isn't allowed but we handle it anyway so
+ // that we won't be confused by incorrect code.
+ if ((field.isFinal || field.isConst) &&
+ !field.isStatic &&
+ field.initializer != null) {
+ callback(field);
+ }
+ }
+ for (ParameterElement parameterElement in constant.parameters) {
+ callback(parameterElement);
}
- }
- for (ParameterElement parameterElement in constant.parameters) {
- callback(parameterElement);
}
} else if (constant is ConstantEvaluationTarget_Annotation) {
Annotation constNode = constant.annotation;
@@ -486,7 +510,7 @@ class ConstantEvaluationEngine {
// The annotation is a reference to a compile-time constant variable,
// so it depends on the variable.
callback(element.variable);
- } else if (element is ConstructorElementImpl && element.isConst) {
+ } else if (element is ConstructorElementImpl) {
// The annotation is a constructor invocation, so it depends on the
// constructor.
callback(element);
@@ -4979,7 +5003,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
super.visitRedirectingConstructorInvocation(node);
ConstructorElement target =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
- if (target != null && target.isConst) {
+ if (target != null) {
_callback(target);
}
return null;
@@ -4992,9 +5016,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
element = (element as PropertyAccessorElement).variable;
}
if (element is VariableElement) {
- if (element.isConst) {
- _callback(element);
- }
+ _callback(element);
}
return null;
}
@@ -5004,7 +5026,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
super.visitSuperConstructorInvocation(node);
ConstructorElement constructor =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
- if (constructor != null && constructor.isConst) {
+ if (constructor != null) {
_callback(constructor);
}
return null;
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/all_the_rest_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698