Skip to content

Commit 0939f5b

Browse files
author
John Messerly
committed
Revert "Revert "fix #26141, add support for type arguments to constants""
This reverts commit bd6669e. [email protected] Review URL: https://codereview.chromium.org/2196483002 .
1 parent 74bbc7f commit 0939f5b

File tree

6 files changed

+205
-47
lines changed

6 files changed

+205
-47
lines changed

pkg/analyzer/lib/src/dart/ast/utilities.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class AstCloner implements AstVisitor<AstNode> {
7272
* [nodes].
7373
*/
7474
List<AstNode/*=E*/ > cloneNodeList/*<E extends AstNode>*/(
75-
NodeList/*<E>*/ nodes) {
75+
List/*<E>*/ nodes) {
7676
int count = nodes.length;
7777
List/*<E>*/ clonedNodes = new List/*<E>*/();
7878
for (int i = 0; i < count; i++) {

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 125 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,22 @@ class ConstantEvaluationEngine {
7575
*/
7676
final ConstantEvaluationValidator validator;
7777

78+
/** Whether we are running in strong mode. */
79+
final bool strongMode;
80+
7881
/**
7982
* Initialize a newly created [ConstantEvaluationEngine]. The [typeProvider]
8083
* is used to access known types. [_declaredVariables] is the set of
8184
* variables declared on the command line using '-D'. The [validator], if
8285
* given, is used to verify correct dependency analysis when running unit
8386
* tests.
8487
*/
85-
ConstantEvaluationEngine(this.typeProvider, this._declaredVariables,
88+
ConstantEvaluationEngine(TypeProvider typeProvider, this._declaredVariables,
8689
{ConstantEvaluationValidator validator, TypeSystem typeSystem})
87-
: validator =
90+
: typeProvider = typeProvider,
91+
strongMode =
92+
typeProvider.objectType.element.context.analysisOptions.strongMode,
93+
validator =
8894
validator ?? new ConstantEvaluationValidator_ForProduction(),
8995
typeSystem = typeSystem ?? new TypeSystemImpl();
9096

@@ -409,7 +415,7 @@ class ConstantEvaluationEngine {
409415

410416
DartObjectImpl evaluateConstructorCall(
411417
AstNode node,
412-
NodeList<Expression> arguments,
418+
List<Expression> arguments,
413419
ConstructorElement constructor,
414420
ConstantVisitor constantVisitor,
415421
ErrorReporter errorReporter) {
@@ -515,25 +521,49 @@ class ConstantEvaluationEngine {
515521
// so consider it an unknown value to suppress further errors.
516522
return new DartObjectImpl.validWithUnknownValue(definingClass);
517523
}
518-
HashMap<String, DartObjectImpl> fieldMap =
519-
new HashMap<String, DartObjectImpl>();
524+
525+
// In strong mode, we allow constants to have type arguments.
526+
//
527+
// They will be added to the lexical environment when evaluating
528+
// subexpressions.
529+
HashMap<String, DartObjectImpl> typeArgumentMap;
530+
if (strongMode) {
531+
// Instantiate the constructor with the in-scope type arguments.
532+
definingClass = constantVisitor.evaluateType(definingClass);
533+
constructor = ConstructorMember.from(constructorBase, definingClass);
534+
535+
typeArgumentMap = new HashMap<String, DartObjectImpl>.fromIterables(
536+
definingClass.typeParameters.map((t) => t.name),
537+
definingClass.typeArguments.map(constantVisitor.typeConstant));
538+
}
539+
540+
var fieldMap = new HashMap<String, DartObjectImpl>();
541+
var fieldInitVisitor = new ConstantVisitor(this, errorReporter,
542+
lexicalEnvironment: typeArgumentMap);
520543
// Start with final fields that are initialized at their declaration site.
521-
for (FieldElement field in constructor.enclosingElement.fields) {
544+
List<FieldElement> fields = constructor.enclosingElement.fields;
545+
for (int i = 0; i < fields.length; i++) {
546+
FieldElement field = fields[i];
522547
if ((field.isFinal || field.isConst) &&
523548
!field.isStatic &&
524549
field is ConstFieldElementImpl) {
525550
validator.beforeGetFieldEvaluationResult(field);
526-
EvaluationResultImpl evaluationResult = field.evaluationResult;
551+
552+
DartObjectImpl fieldValue;
553+
if (strongMode) {
554+
fieldValue = field.constantInitializer.accept(fieldInitVisitor);
555+
} else {
556+
fieldValue = field.evaluationResult?.value;
557+
}
527558
// It is possible that the evaluation result is null.
528559
// This happens for example when we have duplicate fields.
529560
// class Test {final x = 1; final x = 2; const Test();}
530-
if (evaluationResult == null) {
561+
if (fieldValue == null) {
531562
continue;
532563
}
533564
// Match the value and the type.
534565
DartType fieldType =
535566
FieldMember.from(field, constructor.returnType).type;
536-
DartObjectImpl fieldValue = evaluationResult.value;
537567
if (fieldValue != null && !runtimeTypeMatch(fieldValue, fieldType)) {
538568
errorReporter.reportErrorForNode(
539569
CheckedModeCompileTimeErrorCode
@@ -549,6 +579,7 @@ class ConstantEvaluationEngine {
549579
new HashMap<String, DartObjectImpl>();
550580
List<ParameterElement> parameters = constructor.parameters;
551581
int parameterCount = parameters.length;
582+
552583
for (int i = 0; i < parameterCount; i++) {
553584
ParameterElement parameter = parameters[i];
554585
ParameterElement baseParameter = parameter;
@@ -574,12 +605,23 @@ class ConstantEvaluationEngine {
574605
// The parameter is an optional positional parameter for which no value
575606
// was provided, so use the default value.
576607
validator.beforeGetParameterDefault(baseParameter);
577-
EvaluationResultImpl evaluationResult = baseParameter.evaluationResult;
578-
if (evaluationResult == null) {
579-
// No default was provided, so the default value is null.
580-
argumentValue = typeProvider.nullObject;
581-
} else if (evaluationResult.value != null) {
582-
argumentValue = evaluationResult.value;
608+
if (strongMode && baseParameter is ConstVariableElement) {
609+
var defaultValue =
610+
(baseParameter as ConstVariableElement).constantInitializer;
611+
if (defaultValue == null) {
612+
argumentValue = typeProvider.nullObject;
613+
} else {
614+
argumentValue = defaultValue.accept(fieldInitVisitor);
615+
}
616+
} else {
617+
EvaluationResultImpl evaluationResult =
618+
baseParameter.evaluationResult;
619+
if (evaluationResult == null) {
620+
// No default was provided, so the default value is null.
621+
argumentValue = typeProvider.nullObject;
622+
} else if (evaluationResult.value != null) {
623+
argumentValue = evaluationResult.value;
624+
}
583625
}
584626
}
585627
if (argumentValue != null) {
@@ -677,6 +719,7 @@ class ConstantEvaluationEngine {
677719
if (superArguments == null) {
678720
superArguments = new NodeList<Expression>(null);
679721
}
722+
680723
evaluateSuperConstructorCall(node, fieldMap, superConstructor,
681724
superArguments, initializerVisitor, errorReporter);
682725
}
@@ -688,7 +731,7 @@ class ConstantEvaluationEngine {
688731
AstNode node,
689732
HashMap<String, DartObjectImpl> fieldMap,
690733
ConstructorElement superConstructor,
691-
NodeList<Expression> superArguments,
734+
List<Expression> superArguments,
692735
ConstantVisitor initializerVisitor,
693736
ErrorReporter errorReporter) {
694737
if (superConstructor != null && superConstructor.isConst) {
@@ -1231,6 +1274,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12311274
// problem - the error has already been reported.
12321275
return null;
12331276
}
1277+
12341278
return evaluationEngine.evaluateConstructorCall(
12351279
node, node.argumentList.arguments, constructor, this, _errorReporter);
12361280
}
@@ -1274,9 +1318,9 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12741318
return null;
12751319
}
12761320
DartType elementType = _typeProvider.dynamicType;
1277-
if (node.typeArguments != null &&
1278-
node.typeArguments.arguments.length == 1) {
1279-
DartType type = node.typeArguments.arguments[0].type;
1321+
NodeList<TypeName> typeArgs = node.typeArguments?.arguments;
1322+
if (typeArgs?.length == 1) {
1323+
DartType type = visitTypeName(typeArgs[0])?.toTypeValue();
12801324
if (type != null) {
12811325
elementType = type;
12821326
}
@@ -1309,13 +1353,13 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
13091353
}
13101354
DartType keyType = _typeProvider.dynamicType;
13111355
DartType valueType = _typeProvider.dynamicType;
1312-
if (node.typeArguments != null &&
1313-
node.typeArguments.arguments.length == 2) {
1314-
DartType keyTypeCandidate = node.typeArguments.arguments[0].type;
1356+
NodeList<TypeName> typeArgs = node.typeArguments?.arguments;
1357+
if (typeArgs?.length == 2) {
1358+
DartType keyTypeCandidate = visitTypeName(typeArgs[0])?.toTypeValue();
13151359
if (keyTypeCandidate != null) {
13161360
keyType = keyTypeCandidate;
13171361
}
1318-
DartType valueTypeCandidate = node.typeArguments.arguments[1].type;
1362+
DartType valueTypeCandidate = visitTypeName(typeArgs[1])?.toTypeValue();
13191363
if (valueTypeCandidate != null) {
13201364
valueType = valueTypeCandidate;
13211365
}
@@ -1465,6 +1509,57 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
14651509
_typeProvider.symbolType, new SymbolState(buffer.toString()));
14661510
}
14671511

1512+
@override
1513+
DartObjectImpl visitTypeName(TypeName node) {
1514+
DartType type = evaluateType(node.type);
1515+
if (type == null) {
1516+
return super.visitTypeName(node);
1517+
}
1518+
return typeConstant(type);
1519+
}
1520+
1521+
/**
1522+
* Given a [type], returns the constant value that contains that type value.
1523+
*/
1524+
DartObjectImpl typeConstant(DartType type) {
1525+
return new DartObjectImpl(_typeProvider.typeType, new TypeState(type));
1526+
}
1527+
1528+
/**
1529+
* Given a [type] that may contain free type variables, evaluate them against
1530+
* the current lexical environment and return the substituted type.
1531+
*/
1532+
DartType evaluateType(DartType type) {
1533+
if (type is TypeParameterType) {
1534+
// Constants may only refer to type parameters in strong mode.
1535+
if (!evaluationEngine.strongMode) {
1536+
return null;
1537+
}
1538+
1539+
String name = type.name;
1540+
if (_lexicalEnvironment != null) {
1541+
return _lexicalEnvironment[name]?.toTypeValue() ?? type;
1542+
}
1543+
return type;
1544+
}
1545+
if (type is ParameterizedType) {
1546+
List<DartType> typeArguments;
1547+
for (int i = 0; i < type.typeArguments.length; i++) {
1548+
DartType ta = type.typeArguments[i];
1549+
DartType t = evaluateType(ta);
1550+
if (!identical(t, ta)) {
1551+
if (typeArguments == null) {
1552+
typeArguments = type.typeArguments.toList(growable: false);
1553+
}
1554+
typeArguments[i] = t;
1555+
}
1556+
}
1557+
if (typeArguments == null) return type;
1558+
return type.substitute2(typeArguments, type.typeArguments);
1559+
}
1560+
return type;
1561+
}
1562+
14681563
/**
14691564
* Create an error associated with the given [node]. The error will have the
14701565
* given error [code].
@@ -1497,10 +1592,13 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
14971592
}
14981593
return new DartObjectImpl(functionType, new FunctionState(function));
14991594
}
1500-
} else if (variableElement is ClassElement ||
1501-
variableElement is FunctionTypeAliasElement ||
1502-
variableElement is DynamicElementImpl) {
1503-
return new DartObjectImpl(_typeProvider.typeType, new TypeState(element));
1595+
} else if (variableElement is TypeDefiningElement) {
1596+
// Constants may only refer to type parameters in strong mode.
1597+
if (evaluationEngine.strongMode ||
1598+
variableElement is! TypeParameterElement) {
1599+
return new DartObjectImpl(
1600+
_typeProvider.typeType, new TypeState(variableElement.type));
1601+
}
15041602
}
15051603
// TODO(brianwilkerson) Figure out which error to report.
15061604
_error(node, null);

pkg/analyzer/lib/src/dart/constant/value.dart

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -720,10 +720,7 @@ class DartObjectImpl implements DartObject {
720720
DartType toTypeValue() {
721721
InstanceState state = _state;
722722
if (state is TypeState) {
723-
Element element = state._element;
724-
if (element is TypeDefiningElement) {
725-
return element.type;
726-
}
723+
return state._type;
727724
}
728725
return null;
729726
}
@@ -2771,29 +2768,29 @@ class TypeState extends InstanceState {
27712768
/**
27722769
* The element representing the type being modeled.
27732770
*/
2774-
final Element _element;
2771+
final DartType _type;
27752772

27762773
/**
27772774
* Initialize a newly created state to represent the given [value].
27782775
*/
2779-
TypeState(this._element);
2776+
TypeState(this._type);
27802777

27812778
@override
2782-
int get hashCode => _element == null ? 0 : _element.hashCode;
2779+
int get hashCode => _type?.hashCode ?? 0;
27832780

27842781
@override
27852782
String get typeName => "Type";
27862783

27872784
@override
27882785
bool operator ==(Object object) =>
2789-
object is TypeState && (_element == object._element);
2786+
object is TypeState && (_type == object._type);
27902787

27912788
@override
27922789
StringState convertToString() {
2793-
if (_element == null) {
2790+
if (_type == null) {
27942791
return StringState.UNKNOWN_VALUE;
27952792
}
2796-
return new StringState(_element.name);
2793+
return new StringState(_type.displayName);
27972794
}
27982795

27992796
@override
@@ -2804,21 +2801,21 @@ class TypeState extends InstanceState {
28042801

28052802
@override
28062803
BoolState isIdentical(InstanceState rightOperand) {
2807-
if (_element == null) {
2804+
if (_type == null) {
28082805
return BoolState.UNKNOWN_VALUE;
28092806
}
28102807
if (rightOperand is TypeState) {
2811-
Element rightElement = rightOperand._element;
2812-
if (rightElement == null) {
2808+
DartType rightType = rightOperand._type;
2809+
if (rightType == null) {
28132810
return BoolState.UNKNOWN_VALUE;
28142811
}
2815-
return BoolState.from(_element == rightElement);
2812+
return BoolState.from(_type == rightType);
28162813
} else if (rightOperand is DynamicState) {
28172814
return BoolState.UNKNOWN_VALUE;
28182815
}
28192816
return BoolState.FALSE_STATE;
28202817
}
28212818

28222819
@override
2823-
String toString() => _element == null ? "-unknown-" : _element.name;
2820+
String toString() => _type?.toString() ?? "-unknown-";
28242821
}

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,9 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
868868
_checkForConstWithNonConst(node);
869869
_checkForConstWithUndefinedConstructor(
870870
node, constructorName, typeName);
871-
_checkForConstWithTypeParameters(typeName);
871+
if (!_options.strongMode) {
872+
_checkForConstWithTypeParameters(typeName);
873+
}
872874
_checkForConstDeferredClass(node, constructorName, typeName);
873875
} else {
874876
_checkForNewWithUndefinedConstructor(node, constructorName, typeName);
@@ -891,9 +893,9 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
891893
Object visitListLiteral(ListLiteral node) {
892894
TypeArgumentList typeArguments = node.typeArguments;
893895
if (typeArguments != null) {
894-
if (node.constKeyword != null) {
896+
if (!_options.strongMode && node.constKeyword != null) {
895897
NodeList<TypeName> arguments = typeArguments.arguments;
896-
if (arguments.length != 0) {
898+
if (arguments.isNotEmpty) {
897899
_checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
898900
CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_LIST);
899901
}
@@ -910,7 +912,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
910912
TypeArgumentList typeArguments = node.typeArguments;
911913
if (typeArguments != null) {
912914
NodeList<TypeName> arguments = typeArguments.arguments;
913-
if (arguments.length != 0) {
915+
if (!_options.strongMode && arguments.isNotEmpty) {
914916
if (node.constKeyword != null) {
915917
_checkForInvalidTypeArgumentInConstTypedLiteral(arguments,
916918
CompileTimeErrorCode.INVALID_TYPE_ARGUMENT_IN_CONST_MAP);

pkg/analyzer/test/src/dart/constant/evaluation_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ main() {
3030
initializeTestEnvironment();
3131
runReflectiveTests(ConstantValueComputerTest);
3232
runReflectiveTests(ConstantVisitorTest);
33+
runReflectiveTests(StrongConstantValueComputerTest);
3334
}
3435

3536
/**
@@ -1600,3 +1601,11 @@ const b = 3;''');
16001601
return result;
16011602
}
16021603
}
1604+
1605+
@reflectiveTest
1606+
class StrongConstantValueComputerTest extends ConstantValueComputerTest {
1607+
void setUp() {
1608+
super.setUp();
1609+
resetWithOptions(new AnalysisOptionsImpl()..strongMode = true);
1610+
}
1611+
}

0 commit comments

Comments
 (0)