Skip to content

Commit 23d70f6

Browse files
johnniwinthercommit-bot@chromium.org
authored andcommitted
Reland: [cfe] Embed and use allowed experiments in CFE
This CL embeds the sdk_nnbd/lib/_internal/allowed_experiments.json into the CFE and uses this to allow experiments enabled on a per library basis. The file is embedded through generated code to avoid reliance on access to the file itself. A presubmit check is also added to ensure that the json file and the generated code are in sync. This work is in preparation for #41538 This was reverted in https://dart-review.googlesource.com/c/sdk/+/149620 because flutter wasn't prepared for auto-enabling nnbd in dart:* libraries. This has now been fixed in flutter/engine#18714 Closes #42162 Change-Id: I686ed6feaef8ee066b426068fe2a0f5080bf1713 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149588 Reviewed-by: Jens Johansen <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent 5c0c41c commit 23d70f6

27 files changed

+402
-179
lines changed

pkg/_fe_analyzer_shared/lib/src/sdk/allowed_experiments.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class AllowedExperiments {
2727
/// are enabled for all files of this package.
2828
final Map<String, List<String>> packageExperiments;
2929

30-
const AllowedExperiments({
30+
AllowedExperiments({
3131
@required this.sdkDefaultExperiments,
3232
@required this.sdkLibraryExperiments,
3333
@required this.packageExperiments,

pkg/front_end/lib/src/api_prototype/compiler_options.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import '../base/nnbd_mode.dart';
1616

1717
import 'experimental_flags.dart'
1818
show
19+
AllowedExperimentalFlags,
1920
defaultExperimentalFlags,
2021
ExperimentalFlag,
2122
expiredExperimentalFlags,
@@ -129,6 +130,8 @@ class CompilerOptions {
129130
/// Features not mentioned in the map will have their default value.
130131
Map<ExperimentalFlag, bool> experimentalFlags = <ExperimentalFlag, bool>{};
131132

133+
AllowedExperimentalFlags allowedExperimentalFlags;
134+
132135
/// Environment map used when evaluating `bool.fromEnvironment`,
133136
/// `int.fromEnvironment` and `String.fromEnvironment` during constant
134137
/// evaluation. If the map is `null`, all environment constants will be left
Lines changed: 106 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,117 @@
1-
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
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-
// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
6-
//
7-
// Instead modify 'tools/experimental_features.yaml' and run
8-
// 'dart pkg/front_end/tool/fasta.dart generate-experimental-flags' to update.
9-
10-
import 'package:_fe_analyzer_shared/src/sdk/allowed_experiments.dart';
115
import 'package:kernel/kernel.dart' show Version;
126

13-
enum ExperimentalFlag {
14-
constantUpdate2018,
15-
controlFlowCollections,
16-
extensionMethods,
17-
nonNullable,
18-
nonfunctionTypeAliases,
19-
setLiterals,
20-
spreadCollections,
21-
tripleShift,
22-
variance,
23-
}
7+
part 'experimental_flags_generated.dart';
8+
9+
/// The set of experiments enabled for SDK and packages.
10+
///
11+
/// This are derived from an `allowed_experiments.json` file whose default is
12+
/// located in `sdk_nnbd/lib/_internal/allowed_experiments.json`.
13+
class AllowedExperimentalFlags {
14+
/// The set of experiments that are enabled for all SDK libraries other than
15+
/// for those which are specified in [sdkLibraryExperiments].
16+
final Set<ExperimentalFlag> sdkDefaultExperiments;
17+
18+
/// Mapping from individual SDK libraries, e.g. 'core', to the set of
19+
/// experiments that are enabled for this library.
20+
final Map<String, Set<ExperimentalFlag>> sdkLibraryExperiments;
2421

25-
const Version enableConstantUpdate2018Version = const Version(2, 4);
26-
const Version enableControlFlowCollectionsVersion = const Version(2, 2);
27-
const Version enableExtensionMethodsVersion = const Version(2, 6);
28-
const Version enableNonNullableVersion = const Version(2, 9);
29-
const Version enableNonfunctionTypeAliasesVersion = const Version(2, 9);
30-
const Version enableSetLiteralsVersion = const Version(2, 2);
31-
const Version enableSpreadCollectionsVersion = const Version(2, 2);
32-
const Version enableTripleShiftVersion = const Version(2, 9);
33-
const Version enableVarianceVersion = const Version(2, 9);
22+
/// Mapping from package names, e.g. 'path', to the set of experiments that
23+
/// are enabled for all files of this package.
24+
final Map<String, Set<ExperimentalFlag>> packageExperiments;
3425

35-
ExperimentalFlag parseExperimentalFlag(String flag) {
36-
switch (flag) {
37-
case "constant-update-2018":
38-
return ExperimentalFlag.constantUpdate2018;
39-
case "control-flow-collections":
40-
return ExperimentalFlag.controlFlowCollections;
41-
case "extension-methods":
42-
return ExperimentalFlag.extensionMethods;
43-
case "non-nullable":
44-
return ExperimentalFlag.nonNullable;
45-
case "nonfunction-type-aliases":
46-
return ExperimentalFlag.nonfunctionTypeAliases;
47-
case "set-literals":
48-
return ExperimentalFlag.setLiterals;
49-
case "spread-collections":
50-
return ExperimentalFlag.spreadCollections;
51-
case "triple-shift":
52-
return ExperimentalFlag.tripleShift;
53-
case "variance":
54-
return ExperimentalFlag.variance;
26+
const AllowedExperimentalFlags({
27+
this.sdkDefaultExperiments: const {},
28+
this.sdkLibraryExperiments: const {},
29+
this.packageExperiments: const {},
30+
});
31+
32+
/// Return the set of enabled experiments for the package with the [name],
33+
/// e.g. "path", possibly `null`.
34+
Set<ExperimentalFlag> forPackage(String name) {
35+
return packageExperiments[name];
5536
}
56-
return null;
57-
}
5837

59-
const Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
60-
ExperimentalFlag.constantUpdate2018: true,
61-
ExperimentalFlag.controlFlowCollections: true,
62-
ExperimentalFlag.extensionMethods: true,
63-
ExperimentalFlag.nonNullable: false,
64-
ExperimentalFlag.nonfunctionTypeAliases: false,
65-
ExperimentalFlag.setLiterals: true,
66-
ExperimentalFlag.spreadCollections: true,
67-
ExperimentalFlag.tripleShift: false,
68-
ExperimentalFlag.variance: false,
69-
};
38+
/// Return the set of enabled experiments for the library with the [name],
39+
/// e.g. "core".
40+
Set<ExperimentalFlag> forSdkLibrary(String name) {
41+
return sdkLibraryExperiments[name] ?? sdkDefaultExperiments;
42+
}
43+
}
7044

71-
const Map<ExperimentalFlag, bool> expiredExperimentalFlags = {
72-
ExperimentalFlag.constantUpdate2018: true,
73-
ExperimentalFlag.controlFlowCollections: true,
74-
ExperimentalFlag.extensionMethods: false,
75-
ExperimentalFlag.nonNullable: false,
76-
ExperimentalFlag.nonfunctionTypeAliases: false,
77-
ExperimentalFlag.setLiterals: true,
78-
ExperimentalFlag.spreadCollections: true,
79-
ExperimentalFlag.tripleShift: false,
80-
ExperimentalFlag.variance: false,
81-
};
45+
/// Returns `true` if [flag] is enabled using global [experimentalFlags].
46+
///
47+
/// If [experimentalFlags] is `null` or doesn't contain [flag], the default
48+
/// value from [defaultExperimentalFlags] is returned.
49+
///
50+
/// If [flag] is marked as expired in [expiredExperimentalFlags], the value from
51+
/// [defaultExperimentalFlags] is always returned.
52+
bool isExperimentEnabled(ExperimentalFlag flag,
53+
{Map<ExperimentalFlag, bool> experimentalFlags}) {
54+
assert(defaultExperimentalFlags.containsKey(flag),
55+
"No default value for $flag.");
56+
assert(expiredExperimentalFlags.containsKey(flag),
57+
"No expired value for $flag.");
58+
if (expiredExperimentalFlags[flag]) {
59+
return defaultExperimentalFlags[flag];
60+
}
61+
bool enabled;
62+
if (experimentalFlags != null) {
63+
enabled = experimentalFlags[flag];
64+
}
65+
enabled ??= defaultExperimentalFlags[flag];
66+
return enabled;
67+
}
8268

83-
const AllowedExperiments allowedExperiments =
84-
const AllowedExperiments(sdkDefaultExperiments: [
85-
"non-nullable",
86-
], sdkLibraryExperiments: {}, packageExperiments: {});
69+
/// Returns `true` if [flag] is enabled in the library with the [canonicalUri]
70+
/// either globally using [experimentalFlags] or per library using
71+
/// [allowedExperimentalFlags].
72+
///
73+
/// If [experimentalFlags] is `null` or doesn't contain [flag], the default
74+
/// value from [defaultExperimentalFlags] used as the global flag state.
75+
///
76+
/// If [allowedExperimentalFlags] is `null` [defaultAllowedExperimentalFlags] is
77+
/// used for the per library flag state.
78+
///
79+
/// If [flag] is marked as expired in [expiredExperimentalFlags], the value from
80+
/// [defaultExperimentalFlags] is always returned.
81+
///
82+
/// The canonical uri, also known as the import uri, is the absolute uri that
83+
/// defines the identity of a library, for instance `dart:core`, `package:foo`,
84+
/// or `file:///path/dir/file.dart`.
85+
bool isExperimentEnabledInLibrary(ExperimentalFlag flag, Uri canonicalUri,
86+
{Map<ExperimentalFlag, bool> experimentalFlags,
87+
AllowedExperimentalFlags allowedExperimentalFlags}) {
88+
assert(defaultExperimentalFlags.containsKey(flag),
89+
"No default value for $flag.");
90+
assert(expiredExperimentalFlags.containsKey(flag),
91+
"No expired value for $flag.");
92+
if (expiredExperimentalFlags[flag]) {
93+
return defaultExperimentalFlags[flag];
94+
}
95+
bool enabled;
96+
if (experimentalFlags != null) {
97+
enabled = experimentalFlags[flag];
98+
}
99+
enabled ??= defaultExperimentalFlags[flag];
100+
if (!enabled) {
101+
allowedExperimentalFlags ??= defaultAllowedExperimentalFlags;
102+
Set<ExperimentalFlag> allowedFlags;
103+
if (canonicalUri.scheme == 'dart') {
104+
allowedFlags = allowedExperimentalFlags.forSdkLibrary(canonicalUri.path);
105+
} else if (canonicalUri.scheme == 'package') {
106+
int index = canonicalUri.path.indexOf('/');
107+
if (index >= 0) {
108+
String packageName = canonicalUri.path.substring(0, index);
109+
allowedFlags = allowedExperimentalFlags.forPackage(packageName);
110+
}
111+
}
112+
if (allowedFlags != null) {
113+
enabled = allowedFlags.contains(flag);
114+
}
115+
}
116+
return enabled;
117+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
6+
//
7+
// Instead modify 'tools/experimental_features.yaml' and run
8+
// 'dart pkg/front_end/tool/fasta.dart generate-experimental-flags' to update.
9+
10+
part of 'experimental_flags.dart';
11+
12+
enum ExperimentalFlag {
13+
constantUpdate2018,
14+
controlFlowCollections,
15+
extensionMethods,
16+
nonNullable,
17+
nonfunctionTypeAliases,
18+
setLiterals,
19+
spreadCollections,
20+
tripleShift,
21+
variance,
22+
}
23+
24+
const Version enableConstantUpdate2018Version = const Version(2, 4);
25+
const Version enableControlFlowCollectionsVersion = const Version(2, 2);
26+
const Version enableExtensionMethodsVersion = const Version(2, 6);
27+
const Version enableNonNullableVersion = const Version(2, 9);
28+
const Version enableNonfunctionTypeAliasesVersion = const Version(2, 9);
29+
const Version enableSetLiteralsVersion = const Version(2, 2);
30+
const Version enableSpreadCollectionsVersion = const Version(2, 2);
31+
const Version enableTripleShiftVersion = const Version(2, 9);
32+
const Version enableVarianceVersion = const Version(2, 9);
33+
34+
ExperimentalFlag parseExperimentalFlag(String flag) {
35+
switch (flag) {
36+
case "constant-update-2018":
37+
return ExperimentalFlag.constantUpdate2018;
38+
case "control-flow-collections":
39+
return ExperimentalFlag.controlFlowCollections;
40+
case "extension-methods":
41+
return ExperimentalFlag.extensionMethods;
42+
case "non-nullable":
43+
return ExperimentalFlag.nonNullable;
44+
case "nonfunction-type-aliases":
45+
return ExperimentalFlag.nonfunctionTypeAliases;
46+
case "set-literals":
47+
return ExperimentalFlag.setLiterals;
48+
case "spread-collections":
49+
return ExperimentalFlag.spreadCollections;
50+
case "triple-shift":
51+
return ExperimentalFlag.tripleShift;
52+
case "variance":
53+
return ExperimentalFlag.variance;
54+
}
55+
return null;
56+
}
57+
58+
const Map<ExperimentalFlag, bool> defaultExperimentalFlags = {
59+
ExperimentalFlag.constantUpdate2018: true,
60+
ExperimentalFlag.controlFlowCollections: true,
61+
ExperimentalFlag.extensionMethods: true,
62+
ExperimentalFlag.nonNullable: false,
63+
ExperimentalFlag.nonfunctionTypeAliases: false,
64+
ExperimentalFlag.setLiterals: true,
65+
ExperimentalFlag.spreadCollections: true,
66+
ExperimentalFlag.tripleShift: false,
67+
ExperimentalFlag.variance: false,
68+
};
69+
70+
const Map<ExperimentalFlag, bool> expiredExperimentalFlags = {
71+
ExperimentalFlag.constantUpdate2018: true,
72+
ExperimentalFlag.controlFlowCollections: true,
73+
ExperimentalFlag.extensionMethods: false,
74+
ExperimentalFlag.nonNullable: false,
75+
ExperimentalFlag.nonfunctionTypeAliases: false,
76+
ExperimentalFlag.setLiterals: true,
77+
ExperimentalFlag.spreadCollections: true,
78+
ExperimentalFlag.tripleShift: false,
79+
ExperimentalFlag.variance: false,
80+
};
81+
82+
const AllowedExperimentalFlags defaultAllowedExperimentalFlags =
83+
const AllowedExperimentalFlags(sdkDefaultExperiments: {
84+
ExperimentalFlag.nonNullable,
85+
}, sdkLibraryExperiments: {}, packageExperiments: {});

pkg/front_end/lib/src/base/processed_options.dart

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import 'package:package_config/package_config.dart';
2222
import '../api_prototype/compiler_options.dart'
2323
show CompilerOptions, DiagnosticMessage;
2424

25-
import '../api_prototype/experimental_flags.dart'
26-
show defaultExperimentalFlags, ExperimentalFlag, expiredExperimentalFlags;
25+
import '../api_prototype/experimental_flags.dart' as flags;
2726

2827
import '../api_prototype/file_system.dart'
2928
show FileSystem, FileSystemEntity, FileSystemException;
@@ -325,15 +324,27 @@ class ProcessedOptions {
325324
Target get target =>
326325
_target ??= _raw.target ?? new NoneTarget(new TargetFlags());
327326

328-
bool isExperimentEnabled(ExperimentalFlag flag) {
329-
assert(defaultExperimentalFlags.containsKey(flag),
330-
"No default value for $flag.");
331-
assert(expiredExperimentalFlags.containsKey(flag),
332-
"No expired value for $flag.");
333-
if (expiredExperimentalFlags[flag]) {
334-
return defaultExperimentalFlags[flag];
335-
}
336-
return _raw.experimentalFlags[flag] ?? defaultExperimentalFlags[flag];
327+
/// Returns `true` if the [flag] is enabled globally.
328+
///
329+
/// This is `true` either if the [flag] is passed through an explicit
330+
/// `--enable-experiment` option or if the [flag] is expired and on by
331+
/// default.
332+
bool isExperimentEnabledGlobally(flags.ExperimentalFlag flag) {
333+
return flags.isExperimentEnabled(flag,
334+
experimentalFlags: _raw.experimentalFlags);
335+
}
336+
337+
/// Returns `true` if the [flag] is enabled in the library with the given
338+
/// [importUri].
339+
///
340+
/// This is `true` either if the [flag] is enabled globally as defined
341+
/// by [isExperimentEnabledGlobally] or is explicitly enabled through
342+
/// the 'allowed_experiments.json' file for this library.
343+
bool isExperimentEnabledInLibrary(
344+
flags.ExperimentalFlag flag, Uri importUri) {
345+
return flags.isExperimentEnabledInLibrary(flag, importUri,
346+
experimentalFlags: _raw.experimentalFlags,
347+
allowedExperimentalFlags: _raw.allowedExperimentalFlags);
337348
}
338349

339350
/// Get an outline component that summarizes the SDK, if any.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5518,7 +5518,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
55185518
TypeVariableBuilder variable = typeVariables[index];
55195519
variable.bound = bound?.builder;
55205520
if (variance != null) {
5521-
if (!libraryBuilder.loader.target.enableVariance) {
5521+
if (!libraryBuilder.enableVarianceInLibrary) {
55225522
reportVarianceModifierNotEnabled(variance);
55235523
}
55245524
variable.variance = Variance.fromString(variance.lexeme);

0 commit comments

Comments
 (0)