@@ -76,6 +76,8 @@ import 'redirecting_factory_body.dart'
7676
7777import '../names.dart' ;
7878
79+ import 'constness_evaluator.dart' show evaluateConstness, ConstnessEffect;
80+
7981import 'fasta_accessors.dart' ;
8082
8183import 'kernel_api.dart' ;
@@ -183,6 +185,12 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
183185 /// and where that was.
184186 Map <String , int > initializedFields;
185187
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+
186194 BodyBuilder (
187195 KernelLibraryBuilder library,
188196 this .member,
@@ -685,6 +693,50 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
685693 unhandled ("${builder .runtimeType }" , "finishFunction" , builder.charOffset,
686694 builder.fileUri);
687695 }
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 ();
688740 }
689741
690742 @override
@@ -2132,6 +2184,20 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
21322184 functionNestingLevel-- ;
21332185 }
21342186
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+
21352201 @override
21362202 void handleValuedFormalParameter (Token equals, Token token) {
21372203 debugEvent ("ValuedFormalParameter" );
@@ -2448,38 +2514,57 @@ class BodyBuilder<Arguments> extends ScopeListener<JumpTarget>
24482514 argMessage: argMessage);
24492515 }
24502516 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) {
24562519 return buildCompileTimeError (
24572520 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) {
24592526 return deprecated_buildCompileTimeError (
24602527 "Not a const constructor." , charOffset);
24612528 }
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),
24642534 isConst: isConst)
24652535 ..fileOffset = charOffset;
2466- } else {
2467- Procedure procedure = target;
2468- isConst = isConst ||
2469- constantContext != ConstantContext .none && procedure.isConst;
24702536 if (constness == Constness .implicit &&
2471- procedure .isConst &&
2537+ target .isConst &&
24722538 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;
24832568 } else {
24842569 return new ShadowStaticInvocation (
24852570 target, forest.castArguments (arguments),
0 commit comments