Skip to content

Commit 0fb2d0b

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
Finish the kernel2kernel "constants" transformation by fixing a whole bunch of issues
Change-Id: I24e2ff06db1a7555f5091d0c27060ed79d6787ee Reviewed-on: https://dart-review.googlesource.com/31980 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent d68de4e commit 0fb2d0b

File tree

5 files changed

+267
-39
lines changed

5 files changed

+267
-39
lines changed

pkg/kernel/bin/transform.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,11 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
9898
program = coq.transformProgram(coreTypes, program);
9999
break;
100100
case 'constants':
101-
final VmConstantsBackend backend = new VmConstantsBackend(coreTypes);
101+
// We use the -D defines supplied to this VM instead of explicitly using a
102+
// constructed map of constants.
103+
final Map<String, String> defines = null;
104+
final VmConstantsBackend backend =
105+
new VmConstantsBackend(defines, coreTypes);
102106
program = constants.transformProgram(program, backend);
103107
break;
104108
case 'treeshake':

pkg/kernel/lib/transformations/constants.dart

Lines changed: 190 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,63 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
/// This library implements a kernel2kernel constant evaluation transformation.
6+
///
7+
/// Even though it is expected that the frontend does not emit kernel AST which
8+
/// contains compile-time errors, this transformation still performs some
9+
/// valiation and throws a [ConstantEvaluationError] if there was a compile-time
10+
/// errors.
11+
///
12+
/// Due to the lack information which is is only available in the front-end,
13+
/// this validation is incomplete (e.g. whether an integer literal used the
14+
/// hexadecimal syntax or not).
15+
///
16+
/// Furthermore due to the lowering of certain constructs in the front-end
17+
/// (e.g. '??') we need to support a super-set of the normal constant expression
18+
/// language. Issue(http://dartbug.com/31799)
519
library kernel.transformations.constants;
620

721
import '../kernel.dart';
822
import '../ast.dart';
23+
import '../core_types.dart';
924
import '../type_algebra.dart';
25+
import '../type_environment.dart';
26+
import '../class_hierarchy.dart';
1027
import 'treeshaker.dart' show findNativeName;
1128

1229
Program transformProgram(Program program, ConstantsBackend backend,
13-
{bool keepFields: false}) {
14-
transformLibraries(program.libraries, backend, keepFields: keepFields);
30+
{bool keepFields: false,
31+
bool strongMode: false,
32+
bool enableAsserts: false,
33+
CoreTypes coreTypes,
34+
ClassHierarchy hierarchy}) {
35+
coreTypes ??= new CoreTypes(program);
36+
hierarchy ??= new ClosedWorldClassHierarchy(program);
37+
38+
final typeEnvironment =
39+
new TypeEnvironment(coreTypes, hierarchy, strongMode: strongMode);
40+
41+
transformLibraries(program.libraries, backend, coreTypes, typeEnvironment,
42+
keepFields: keepFields,
43+
strongMode: strongMode,
44+
enableAsserts: enableAsserts);
1545
return program;
1646
}
1747

1848
void transformLibraries(List<Library> libraries, ConstantsBackend backend,
19-
{bool keepFields: false, bool keepVariables: false}) {
20-
final ConstantsTransformer constantsTransformer =
21-
new ConstantsTransformer(backend, keepFields, keepVariables);
49+
CoreTypes coreTypes, TypeEnvironment typeEnvironment,
50+
{bool keepFields: false,
51+
bool keepVariables: false,
52+
bool strongMode: false,
53+
bool enableAsserts: false}) {
54+
final ConstantsTransformer constantsTransformer = new ConstantsTransformer(
55+
backend,
56+
keepFields,
57+
keepVariables,
58+
coreTypes,
59+
typeEnvironment,
60+
strongMode,
61+
enableAsserts);
2262
for (final Library library in libraries) {
2363
for (final Field field in library.fields.toList()) {
2464
constantsTransformer.convertField(field);
@@ -39,19 +79,39 @@ void transformLibraries(List<Library> libraries, ConstantsBackend backend,
3979
constantsTransformer.convertConstructor(constructor);
4080
}
4181
}
82+
for (final Typedef td in library.typedefs) {
83+
constantsTransformer.convertTypedef(td);
84+
}
85+
86+
if (!keepFields) {
87+
// The transformer API does not iterate over `Library.additionalExports`,
88+
// so we manually delete the references to shaken nodes.
89+
library.additionalExports.removeWhere((Reference reference) {
90+
return reference.canonicalName == null;
91+
});
92+
}
4293
}
4394
}
4495

4596
class ConstantsTransformer extends Transformer {
4697
final ConstantEvaluator constantEvaluator;
98+
final CoreTypes coreTypes;
99+
final TypeEnvironment typeEnvironment;
47100

48101
/// Whether to preserve constant [Field]s. All use-sites will be rewritten.
49102
final bool keepFields;
50103
final bool keepVariables;
51104

52105
ConstantsTransformer(
53-
ConstantsBackend backend, this.keepFields, this.keepVariables)
54-
: constantEvaluator = new ConstantEvaluator(backend);
106+
ConstantsBackend backend,
107+
this.keepFields,
108+
this.keepVariables,
109+
this.coreTypes,
110+
this.typeEnvironment,
111+
bool strongMode,
112+
bool enableAsserts)
113+
: constantEvaluator = new ConstantEvaluator(
114+
backend, typeEnvironment, coreTypes, strongMode, enableAsserts);
55115

56116
// Transform the library/class members:
57117

@@ -79,6 +139,13 @@ class ConstantsTransformer extends Transformer {
79139
});
80140
}
81141

142+
void convertTypedef(Typedef td) {
143+
// A typedef can have annotations on variables which are constants.
144+
constantEvaluator.withNewEnvironment(() {
145+
td.accept(this);
146+
});
147+
}
148+
82149
// Handle definition of constants:
83150

84151
visitVariableDeclaration(VariableDeclaration node) {
@@ -166,6 +233,10 @@ class ConstantsTransformer extends Transformer {
166233

167234
class ConstantEvaluator extends RecursiveVisitor {
168235
final ConstantsBackend backend;
236+
final CoreTypes coreTypes;
237+
final TypeEnvironment typeEnvironment;
238+
final bool strongMode;
239+
final bool enableAsserts;
169240

170241
final Map<Constant, Constant> canonicalizationCache;
171242
final Map<Node, Constant> nodeCache;
@@ -177,7 +248,8 @@ class ConstantEvaluator extends RecursiveVisitor {
177248
InstanceBuilder instanceBuilder;
178249
EvaluationEnvironment env;
179250

180-
ConstantEvaluator(this.backend)
251+
ConstantEvaluator(this.backend, this.typeEnvironment, this.coreTypes,
252+
this.strongMode, this.enableAsserts)
181253
: canonicalizationCache = <Constant, Constant>{},
182254
nodeCache = <Node, Constant>{};
183255

@@ -204,6 +276,8 @@ class ConstantEvaluator extends RecursiveVisitor {
204276
}
205277

206278
visitIntLiteral(IntLiteral node) {
279+
// The frontend will ensure the integer literals are in signed 64-bit range
280+
// in strong mode.
207281
return canonicalize(new IntConstant(node.value));
208282
}
209283

@@ -335,6 +409,21 @@ class ConstantEvaluator extends RecursiveVisitor {
335409
typeArguments,
336410
evaluatePositionalArguments(init.arguments),
337411
evaluateNamedArguments(init.arguments));
412+
} else if (init is AssertInitializer) {
413+
if (enableAsserts) {
414+
final Constant condition = init.statement.condition.accept(this);
415+
416+
if (condition is BoolConstant) {
417+
if (!condition.value) {
418+
final Constant message = init.statement.message?.accept(this);
419+
throw new ConstantEvaluationError(
420+
'Assert initializer condition failed with message: $message.');
421+
}
422+
} else {
423+
throw new ConstantEvaluationError(
424+
'Assert initializer did not evaluate to a boolean condition.');
425+
}
426+
}
338427
} else {
339428
throw new ConstantEvaluationError(
340429
'Cannot evaluate constant with [${init.runtimeType}].');
@@ -353,15 +442,19 @@ class ConstantEvaluator extends RecursiveVisitor {
353442

354443
// Handle == and != first (it's common between all types).
355444
if (arguments.length == 1 && node.name.name == '==') {
356-
ensurePrimitiveConstant(receiver);
445+
// TODO(http://dartbug.com/31799): Re-enable these checks.
446+
//ensurePrimitiveConstant(receiver);
357447
final right = arguments[0];
358-
ensurePrimitiveConstant(right);
448+
// TODO(http://dartbug.com/31799): Re-enable these checks.
449+
//ensurePrimitiveConstant(right);
359450
return receiver == right ? trueConstant : falseConstant;
360451
}
361452
if (arguments.length == 1 && node.name.name == '!=') {
362-
ensurePrimitiveConstant(receiver);
453+
// TODO(http://dartbug.com/31799): Re-enable these checks.
454+
//ensurePrimitiveConstant(receiver);
363455
final right = arguments[0];
364-
ensurePrimitiveConstant(right);
456+
// TODO(http://dartbug.com/31799): Re-enable these checks.
457+
//ensurePrimitiveConstant(right);
365458
return receiver != right ? trueConstant : falseConstant;
366459
}
367460

@@ -498,12 +591,14 @@ class ConstantEvaluator extends RecursiveVisitor {
498591
}
499592

500593
visitConditionalExpression(ConditionalExpression node) {
501-
final BoolConstant constant = evaluate(node.condition);
594+
final Constant constant = evaluate(node.condition);
502595
if (constant == trueConstant) {
503596
return evaluate(node.then);
504-
} else {
505-
assert(constant == falseConstant);
597+
} else if (constant == falseConstant) {
506598
return evaluate(node.otherwise);
599+
} else {
600+
throw new ConstantEvaluationError(
601+
'Cannot use $constant as condition in a conditional expression.');
507602
}
508603
}
509604

@@ -593,14 +688,19 @@ class ConstantEvaluator extends RecursiveVisitor {
593688
} else if (target.name.name == 'identical') {
594689
// Ensure the "identical()" function comes from dart:core.
595690
final parent = target.parent;
596-
if (parent is Library && parent.importUri == 'dart:core') {
691+
if (parent is Library && parent == coreTypes.coreLibrary) {
597692
final positionalArguments =
598693
evaluatePositionalArguments(node.arguments);
599-
final left = positionalArguments[0];
600-
ensurePrimitiveConstant(left);
601-
final right = positionalArguments[1];
602-
ensurePrimitiveConstant(right);
603-
return left == right ? trueConstant : falseConstant;
694+
final Constant left = positionalArguments[0];
695+
// TODO(http://dartbug.com/31799): Re-enable these checks.
696+
//ensurePrimitiveConstant(left);
697+
final Constant right = positionalArguments[1];
698+
// TODO(http://dartbug.com/31799): Re-enable these checks.
699+
//ensurePrimitiveConstant(right);
700+
// Since we canonicalize constants during the evaluation, we can use
701+
// identical here.
702+
assert(left == right);
703+
return identical(left, right) ? trueConstant : falseConstant;
604704
}
605705
}
606706
}
@@ -609,8 +709,63 @@ class ConstantEvaluator extends RecursiveVisitor {
609709
'Calling "$target" during constant evaluation is disallowed.');
610710
}
611711

712+
visitAsExpression(AsExpression node) {
713+
final Constant constant = node.operand.accept(this);
714+
ensureIsSubtype(constant, evaluateDartType(node.type));
715+
return constant;
716+
}
717+
718+
visitNot(Not node) {
719+
final Constant constant = node.operand.accept(this);
720+
if (constant is BoolConstant) {
721+
return constant == trueConstant ? falseConstant : trueConstant;
722+
}
723+
throw new ConstantEvaluationError(
724+
'A not expression must have a boolean operand.');
725+
}
726+
727+
visitSymbolLiteral(SymbolLiteral node) {
728+
final value = canonicalize(new StringConstant(node.value));
729+
return canonicalize(backend.buildSymbolConstant(value));
730+
}
731+
612732
// Helper methods:
613733

734+
void ensureIsSubtype(Constant constant, DartType type) {
735+
DartType constantType;
736+
if (constant is NullConstant) {
737+
constantType = new InterfaceType(coreTypes.nullClass);
738+
} else if (constant is BoolConstant) {
739+
constantType = new InterfaceType(coreTypes.boolClass);
740+
} else if (constant is IntConstant) {
741+
constantType = new InterfaceType(coreTypes.intClass);
742+
} else if (constant is DoubleConstant) {
743+
constantType = new InterfaceType(coreTypes.doubleClass);
744+
} else if (constant is StringConstant) {
745+
constantType = new InterfaceType(coreTypes.stringClass);
746+
} else if (constant is MapConstant) {
747+
constantType = new InterfaceType(
748+
coreTypes.mapClass, <DartType>[constant.keyType, constant.valueType]);
749+
} else if (constant is ListConstant) {
750+
constantType = new InterfaceType(
751+
coreTypes.stringClass, <DartType>[constant.typeArgument]);
752+
} else if (constant is InstanceConstant) {
753+
constantType = new InterfaceType(constant.klass, constant.typeArguments);
754+
} else if (constant is TearOffConstant) {
755+
constantType = constant.procedure.function.functionType;
756+
} else if (constant is TypeLiteralConstant) {
757+
constantType = new InterfaceType(coreTypes.typeClass);
758+
} else {
759+
throw new ConstantEvaluationError(
760+
'No support for obtaining the type of $constant.');
761+
}
762+
763+
if (!typeEnvironment.isSubtypeOf(constantType, type)) {
764+
throw new ConstantEvaluationError(
765+
'Constant $constant is not a subtype of ${type}.');
766+
}
767+
}
768+
614769
List<DartType> evaluateTypeArguments(Arguments arguments) {
615770
return evaluateDartTypes(arguments.types);
616771
}
@@ -686,7 +841,7 @@ class ConstantEvaluator extends RecursiveVisitor {
686841
result = a - b;
687842
break;
688843
case '*':
689-
result = a - b;
844+
result = a * b;
690845
break;
691846
case '/':
692847
result = a / b;
@@ -695,13 +850,13 @@ class ConstantEvaluator extends RecursiveVisitor {
695850
result = a ~/ b;
696851
break;
697852
case '%':
698-
result = a ~/ b;
853+
result = a % b;
699854
break;
700855
}
701856

702857
if (result != null) {
703858
return canonicalize(result is int
704-
? new IntConstant(result)
859+
? new IntConstant(_wrapAroundInteger(result))
705860
: new DoubleConstant(result as double));
706861
}
707862

@@ -719,6 +874,16 @@ class ConstantEvaluator extends RecursiveVisitor {
719874
throw new ConstantEvaluationError(
720875
'Binary operation "$op" on num is disallowed.');
721876
}
877+
878+
int _wrapAroundInteger(int value) {
879+
if (strongMode) {
880+
return value.toSigned(64);
881+
}
882+
return value;
883+
}
884+
885+
static const kMaxInt64 = (1 << 63) - 1;
886+
static const kMinInt64 = -(1 << 63);
722887
}
723888

724889
/// Holds the necessary information for a constant object, namely
@@ -798,6 +963,7 @@ abstract class ConstantsBackend {
798963
List<DartType> typeArguments,
799964
List<Constant> positionalArguments,
800965
Map<String, Constant> namedArguments);
966+
Constant buildSymbolConstant(StringConstant value);
801967

802968
Constant lowerListConstant(ListConstant constant);
803969
Constant lowerMapConstant(MapConstant constant);

0 commit comments

Comments
 (0)