@@ -76,6 +76,8 @@ import 'redirecting_factory_body.dart'
76
76
77
77
import '../names.dart' ;
78
78
79
+ import 'constness_evaluator.dart' show evaluateConstness, ConstnessEffect;
80
+
79
81
import 'fasta_accessors.dart' ;
80
82
81
83
import 'kernel_api.dart' ;
@@ -183,6 +185,12 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
183
185
/// and where that was.
184
186
Map <String , int > initializedFields;
185
187
188
+ /// Constructor invocations (either generative or factory) with not specified
189
+ /// `new` or `const` keywords. The constness for these should be inferred
190
+ /// based on the subexpressions.
191
+ List <Expression > constructorInvocationsWithImplicitConstness =
192
+ new List <Expression >();
193
+
186
194
BodyBuilder (
187
195
KernelLibraryBuilder library,
188
196
this .member,
@@ -685,6 +693,50 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
685
693
unhandled ("${builder .runtimeType }" , "finishFunction" , builder.charOffset,
686
694
builder.fileUri);
687
695
}
696
+
697
+ inferConstness ();
698
+ }
699
+
700
+ // Infers constness of the constructor invocations collected so far in
701
+ // [constructorInvocationsWithImplicitConstness], then clears out the list.
702
+ void inferConstness () {
703
+ // The algorithm below takes advantage of the fact that for each expression
704
+ // that needs constness inference comes after all its subexpressions that
705
+ // also need constness inference in
706
+ // [constructorInvocationsWithImplicitConstness].
707
+ for (Expression invocation in constructorInvocationsWithImplicitConstness) {
708
+ if (invocation is ConstructorInvocation ) {
709
+ ConstnessEffect constness =
710
+ evaluateConstness (invocation, coreTypes).effect;
711
+ if (constness == ConstnessEffect .taintedConst) {
712
+ // TODO(dmitryas): Find a better way to unwrap the error node.
713
+ ShadowSyntheticExpression errorMessage = buildCompileTimeError (
714
+ fasta.messageCantDetermineConstness,
715
+ invocation.fileOffset,
716
+ noLength);
717
+ invocation.replaceWith (errorMessage.desugared);
718
+ } else {
719
+ invocation.isConst = constness == ConstnessEffect .allowedConst;
720
+ }
721
+ } else if (invocation is StaticInvocation ) {
722
+ ConstnessEffect constness =
723
+ evaluateConstness (invocation, coreTypes).effect;
724
+ if (constness == ConstnessEffect .taintedConst) {
725
+ // TODO(dmitryas): Find a better way to unwrap the error node.
726
+ ShadowSyntheticExpression errorMessage = buildCompileTimeError (
727
+ fasta.messageCantDetermineConstness,
728
+ invocation.fileOffset,
729
+ noLength);
730
+ invocation.replaceWith (errorMessage.desugared);
731
+ } else {
732
+ invocation.isConst = constness == ConstnessEffect .allowedConst;
733
+ }
734
+ } else {
735
+ unhandled ("${invocation .runtimeType }" , "inferConstness" ,
736
+ invocation.fileOffset, invocation.location.file);
737
+ }
738
+ }
739
+ constructorInvocationsWithImplicitConstness.clear ();
688
740
}
689
741
690
742
@override
@@ -2132,6 +2184,20 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
2132
2184
functionNestingLevel-- ;
2133
2185
}
2134
2186
2187
+ @override
2188
+ void beginFormalParameterDefaultValueExpression () {
2189
+ super .push (constantContext);
2190
+ constantContext = ConstantContext .needsExplicitConst;
2191
+ }
2192
+
2193
+ @override
2194
+ void endFormalParameterDefaultValueExpression () {
2195
+ debugEvent ("FormalParameterDefaultValueExpression" );
2196
+ var defaultValueExpression = pop ();
2197
+ constantContext = pop ();
2198
+ push (defaultValueExpression);
2199
+ }
2200
+
2135
2201
@override
2136
2202
void handleValuedFormalParameter (Token equals, Token token) {
2137
2203
debugEvent ("ValuedFormalParameter" );
@@ -2448,38 +2514,57 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
2448
2514
argMessage: argMessage);
2449
2515
}
2450
2516
if (target is Constructor ) {
2451
- isConst =
2452
- isConst || constantContext != ConstantContext .none && target.isConst;
2453
- if (constness == Constness .implicit &&
2454
- target.isConst &&
2455
- constantContext != ConstantContext .inferred) {
2517
+ if (constantContext == ConstantContext .needsExplicitConst &&
2518
+ constness == Constness .implicit) {
2456
2519
return buildCompileTimeError (
2457
2520
fasta.messageCantDetermineConstness, charOffset, noLength);
2458
- } else if (isConst && ! target.isConst) {
2521
+ }
2522
+ isConst =
2523
+ isConst || constantContext != ConstantContext .none && target.isConst;
2524
+ if ((isConst || constantContext == ConstantContext .inferred) &&
2525
+ ! target.isConst) {
2459
2526
return deprecated_buildCompileTimeError (
2460
2527
"Not a const constructor." , charOffset);
2461
2528
}
2462
- return new ShadowConstructorInvocation (target, targetTypeArguments,
2463
- initialTarget, forest.castArguments (arguments),
2529
+ ShadowConstructorInvocation invocation = new ShadowConstructorInvocation (
2530
+ target,
2531
+ targetTypeArguments,
2532
+ initialTarget,
2533
+ forest.castArguments (arguments),
2464
2534
isConst: isConst)
2465
2535
..fileOffset = charOffset;
2466
- } else {
2467
- Procedure procedure = target;
2468
- isConst = isConst ||
2469
- constantContext != ConstantContext .none && procedure.isConst;
2470
2536
if (constness == Constness .implicit &&
2471
- procedure .isConst &&
2537
+ target .isConst &&
2472
2538
constantContext != ConstantContext .inferred) {
2473
- return buildCompileTimeError (
2474
- fasta.messageCantDetermineConstness, charOffset, noLength);
2475
- } else if (isConst && ! procedure.isConst) {
2476
- return deprecated_buildCompileTimeError (
2477
- "Not a const factory." , charOffset);
2478
- } else if (procedure.isFactory) {
2479
- return new ShadowFactoryConstructorInvocation (target,
2480
- targetTypeArguments, initialTarget, forest.castArguments (arguments),
2481
- isConst: isConst)
2482
- ..fileOffset = charOffset;
2539
+ constructorInvocationsWithImplicitConstness.add (invocation);
2540
+ }
2541
+ return invocation;
2542
+ } else {
2543
+ Procedure procedure = target;
2544
+ if (procedure.isFactory) {
2545
+ isConst = isConst ||
2546
+ constantContext != ConstantContext .none && procedure.isConst;
2547
+ if ((isConst || constantContext == ConstantContext .inferred) &&
2548
+ ! procedure.isConst) {
2549
+ return deprecated_buildCompileTimeError (
2550
+ "Not a const factory." , charOffset);
2551
+ }
2552
+ if (constantContext == ConstantContext .needsExplicitConst &&
2553
+ constness == Constness .implicit) {
2554
+ return buildCompileTimeError (
2555
+ fasta.messageCantDetermineConstness, charOffset, noLength);
2556
+ }
2557
+ ShadowFactoryConstructorInvocation invocation =
2558
+ new ShadowFactoryConstructorInvocation (target, targetTypeArguments,
2559
+ initialTarget, forest.castArguments (arguments),
2560
+ isConst: isConst)
2561
+ ..fileOffset = charOffset;
2562
+ if (constness == Constness .implicit &&
2563
+ procedure.isConst &&
2564
+ constantContext != ConstantContext .inferred) {
2565
+ constructorInvocationsWithImplicitConstness.add (invocation);
2566
+ }
2567
+ return invocation;
2483
2568
} else {
2484
2569
return new ShadowStaticInvocation (
2485
2570
target, forest.castArguments (arguments),
0 commit comments