Skip to content

Commit 59d8596

Browse files
Dmitry Stefantsovcommit-bot@chromium.org
Dmitry Stefantsov
authored andcommitted
[fasta] Do new/const insertion in simple cases
Bug: http://dartbug.com/32553 Change-Id: I58e201ffd5019a5a65c0ac188aaab363225b5296 Reviewed-on: https://dart-review.googlesource.com/48481 Commit-Queue: Dmitry Stefantsov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 4c0a09b commit 59d8596

29 files changed

+763
-147
lines changed

pkg/analyzer/lib/src/fasta/ast_builder.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,14 @@ class AstBuilder extends ScopeListener {
10041004
popTypedList(count), leftDelimeter, rightDelimeter));
10051005
}
10061006

1007+
@override
1008+
void beginFormalParameterDefaultValueExpression() {}
1009+
1010+
@override
1011+
void endFormalParameterDefaultValueExpression() {
1012+
debugEvent("FormalParameterDefaultValueExpression");
1013+
}
1014+
10071015
void handleValuedFormalParameter(Token equals, Token token) {
10081016
assert(optional('=', equals) || optional(':', equals));
10091017
debugEvent("ValuedFormalParameter");

pkg/front_end/lib/src/fasta/constant_context.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,19 @@ enum ConstantContext {
2323
/// This means that `Object()` and `[]` are equivalent to `const Object()` and
2424
/// `const []` respectively. `new Object()` is a compile-time error.
2525
inferred,
26+
27+
/// In a context that allows only constant values, but requires them to be
28+
/// defined as `const` explicitly. For example, in default values of optional
29+
/// and named parameters.
30+
///
31+
/// The following code should emit a compile-time error:
32+
///
33+
/// class Bar { const Bar(); }
34+
/// class Foo { void foo({Bar bar: Bar()}) {} }
35+
///
36+
/// The following code should compile without errors:
37+
///
38+
/// class Bar { const Bar(); }
39+
/// class Foo { void foo({Bar bar: const Bar()}) {} }
40+
needsExplicitConst,
2641
}

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 108 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ import 'redirecting_factory_body.dart'
7676

7777
import '../names.dart';
7878

79+
import 'constness_evaluator.dart' show evaluateConstness, ConstnessEffect;
80+
7981
import 'fasta_accessors.dart';
8082

8183
import '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

Comments
 (0)