CFE support for compiling individual expressions in a context.

Change-Id: I6469fb16e8846eccaeca3f8585872f4b69dd3f1e
Reviewed-on: https://dart-review.googlesource.com/38123
Reviewed-by: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
Samir Jindel 2018-05-02 12:45:03 +00:00
parent 7d5025e814
commit 9038b8f45f
10 changed files with 361 additions and 87 deletions

View file

@ -4,7 +4,8 @@
import 'dart:async' show Future;
import 'package:kernel/kernel.dart' show Component;
import 'package:kernel/kernel.dart'
show Component, DartType, NamedNode, Procedure, TypeParameter;
import '../base/processed_options.dart' show ProcessedOptions;
@ -12,8 +13,19 @@ import '../fasta/compiler_context.dart' show CompilerContext;
import '../fasta/incremental_compiler.dart' show IncrementalCompiler;
import '../fasta/scanner/string_scanner.dart' show StringScanner;
import 'compiler_options.dart' show CompilerOptions;
/// Identifies a position in the source program for expression compilation.
///
/// Currently this can be either a library-scope or a class-scope. This object
/// may also contain references to FE-internal datastructures, so it is
/// invalidated by any changes to the Kernel program.
abstract class CompilationPosition {
final NamedNode kernelNode = null;
}
abstract class IncrementalKernelGenerator {
factory IncrementalKernelGenerator(CompilerOptions options, Uri entryPoint,
[Uri bootstrapDill]) {
@ -30,4 +42,42 @@ abstract class IncrementalKernelGenerator {
/// valid files. This guarantees that those files will be re-read on the
/// next call to [computeDelta]).
void invalidate(Uri uri);
/// Compile [expression] as an [Expression]. A function returning that
/// expression is compiled.
///
/// [expression] may use the variables supplied in [definitions] as free
/// variables and [typeDefinitions] as free type variables. These will become
/// required parameters to the compiled function. All elements of
/// [definitions] and [typeDefinitions] will become parameters/type
/// parameters, whether or not they appear free in [expression]. The type
/// parameters should have a null parent pointer.
///
/// [enclosingNode] must refer to either a [Library] or a [Class] in which
/// scope [expression] is compiled. If it refers to a [Class], the flag
/// [isStatic] determines whether [expression] may reference "this".
///
/// It is illegal to use "await" in [expression] and the compiled function
/// will always be synchronous.
///
/// [computeDelta] must have been called at least once prior.
///
/// [compileExpression] will return [null] if the library or class for
/// [enclosingNode] could not be found. Otherwise, errors are reported in the
/// normal way.
Future<Procedure> compileExpression(
String expression,
Map<String, DartType> definitions,
List<TypeParameter> typeDefinitions,
CompilationPosition position,
[bool isStatic = false]);
/// Finds the [CompilationPosition] referenced by [library] and optionally
/// [class].
CompilationPosition resolveCompilationPosition(Uri library,
[String className]);
}
bool isLegalIdentifier(String identifier) {
return StringScanner.isLegalIdentifier(identifier);
}

View file

@ -8,13 +8,25 @@ import 'dart:async' show Future;
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
import 'package:kernel/kernel.dart'
show Component, Library, LibraryPart, Procedure, Source;
import '../api_prototype/file_system.dart' show FileSystemEntity;
import 'package:kernel/kernel.dart'
show
AsyncMarker,
Component,
DartType,
DynamicType,
InterfaceType,
Library,
LibraryPart,
NamedNode,
Procedure,
ProcedureKind,
Source,
TypeParameter;
import '../api_prototype/incremental_kernel_generator.dart'
show IncrementalKernelGenerator;
show CompilationPosition, isLegalIdentifier, IncrementalKernelGenerator;
import 'builder/builder.dart' show LibraryBuilder;
@ -31,12 +43,55 @@ import 'kernel/kernel_incremental_target.dart'
import 'library_graph.dart' show LibraryGraph;
import 'kernel/kernel_library_builder.dart' show KernelLibraryBuilder;
import 'source/source_library_builder.dart' show SourceLibraryBuilder;
import 'ticker.dart' show Ticker;
import 'uri_translator.dart' show UriTranslator;
import 'modifier.dart' as Modifier;
import '../scanner/token.dart' show Token;
import 'scanner/error_token.dart' show ErrorToken;
import 'scanner/string_scanner.dart' show StringScanner;
import 'dill/built_type_builder.dart' show BuiltTypeBuilder;
import 'kernel/kernel_formal_parameter_builder.dart'
show KernelFormalParameterBuilder;
import 'kernel/kernel_procedure_builder.dart' show KernelProcedureBuilder;
import 'builder/class_builder.dart' show ClassBuilder;
import 'kernel/kernel_shadow_ast.dart' show ShadowTypeInferenceEngine;
import 'kernel/body_builder.dart' show BodyBuilder;
import 'kernel/kernel_type_variable_builder.dart'
show KernelTypeVariableBuilder;
import 'parser/parser.dart' show Parser;
import 'parser/member_kind.dart' show MemberKind;
import 'scope.dart' show Scope;
import 'type_inference/type_inferrer.dart' show TypeInferrer;
class IncrementalCompilationPosition implements CompilationPosition {
final NamedNode kernelNode;
final LibraryBuilder libraryBuilder; // will not be null
final ClassBuilder classBuilder; // may be null if not inside a class
IncrementalCompilationPosition(
this.kernelNode, this.libraryBuilder, this.classBuilder);
}
class IncrementalCompiler implements IncrementalKernelGenerator {
final CompilerContext context;
@ -46,7 +101,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
DillTarget dillLoadedData;
Map<Uri, Source> dillLoadedDataUriToSource = <Uri, Source>{};
List<LibraryBuilder> platformBuilders;
Map<Uri, LibraryBuilder> platformBuilders;
Map<Uri, LibraryBuilder> userBuilders;
final Uri initializeFromDillUri;
bool initializedFromDill = false;
@ -101,10 +156,10 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
}
summaryBytes = null;
userBuilders = <Uri, LibraryBuilder>{};
platformBuilders = <LibraryBuilder>[];
platformBuilders = <Uri, LibraryBuilder>{};
dillLoadedData.loader.builders.forEach((uri, builder) {
if (builder.uri.scheme == "dart") {
platformBuilders.add(builder);
platformBuilders[uri] = builder;
} else {
userBuilders[uri] = builder;
}
@ -132,7 +187,8 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
ticker.logMs("Decided to reuse ${reusedLibraries.length}"
" of ${userCode.loader.builders.length} libraries");
}
reusedLibraries.addAll(platformBuilders);
reusedLibraries.addAll(platformBuilders.values);
KernelIncrementalTarget userCodeOld = userCode;
userCode = new KernelIncrementalTarget(
@ -168,8 +224,11 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
return new Component(
libraries: compiledLibraries, uriToSource: <Uri, Source>{});
}
userCodeOld?.loader?.builders?.clear();
userCodeOld = null;
if (componentWithDill != null) {
userCodeOld?.loader?.releaseAncillaryResources();
userCodeOld?.loader?.builders?.clear();
userCodeOld = null;
}
List<Library> compiledLibraries =
new List<Library>.from(userCode.loader.libraries);
@ -188,8 +247,10 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
outputLibraries = compiledLibraries;
}
// Clean up.
userCode.loader.releaseAncillaryResources();
if (componentWithDill == null) {
userCode.loader.builders.clear();
userCode = userCodeOld;
}
// This is the incremental component.
return new Component(libraries: outputLibraries, uriToSource: uriToSource)
@ -318,6 +379,194 @@ class IncrementalCompiler implements IncrementalKernelGenerator {
ticker.logMs("Appended libraries");
}
IncrementalCompilationPosition resolveCompilationPosition(Uri libraryUri,
[String className]) {
if (userCode == null || dillLoadedData == null) return null;
// Find library.
LibraryBuilder enclosingLibrary = userCode.loader.builders[libraryUri];
if (enclosingLibrary == null) return null;
if (className == null) {
return new IncrementalCompilationPosition(
enclosingLibrary.target, enclosingLibrary, null);
}
ClassBuilder classBuilder = enclosingLibrary.scopeBuilder[className];
if (classBuilder == null) return null;
return new IncrementalCompilationPosition(
classBuilder.target, enclosingLibrary, classBuilder);
}
@override
Future<Procedure> compileExpression(
String expression,
Map<String, DartType> definitions,
List<TypeParameter> typeDefinitions,
covariant IncrementalCompilationPosition position,
[bool isStatic = false]) async {
assert(dillLoadedData != null && userCode != null);
dillLoadedData.loader.seenMessages.clear();
userCode.loader.seenMessages.clear();
for (TypeParameter typeParam in typeDefinitions) {
if (!isLegalIdentifier(typeParam.name)) return null;
}
for (String name in definitions.keys) {
if (!isLegalIdentifier(name)) return null;
}
String expressionPrefix =
'(${definitions.keys.map((name) => "dynamic $name").join(",")}) =>';
return context.runInContext((CompilerContext c) async {
// Find library builder or report error.
bool inClass = position.classBuilder != null;
LibraryBuilder enclosingLibraryBuilder = position.libraryBuilder;
ClassBuilder classBuilder = position.classBuilder;
Library enclosingLibrary = position.libraryBuilder.target;
dillLoadedData.loader.coreTypes = userCode.loader.coreTypes;
// Create a synthetic KernelLibraryBuilder to hold the compiled procedure.
// This ensures that the uri and character offset positions inside the
// expression refer to the the expression text, and not to random
// positions in the enclosing library's source.
Uri debugExprUri = new Uri(
scheme: "org-dartlang-debug", path: "synthetic_debug_expression");
KernelLibraryBuilder kernelLibraryBuilder = new KernelLibraryBuilder(
debugExprUri,
debugExprUri,
userCode.loader,
/*actualOrigin=*/ null,
enclosingLibrary);
// Parse the function prefix.
StringScanner scanner = new StringScanner(expressionPrefix);
Token startToken = scanner.tokenize();
assert(startToken is! ErrorToken);
assert(!scanner.hasErrors);
// Parse the expression. By parsing the expression separately from the
// function prefix, we ensure that the offsets for tokens coming from the
// expression are correct.
scanner = new StringScanner(expression);
Token expressionStartToken = scanner.tokenize();
while (expressionStartToken is ErrorToken) {
ErrorToken token = expressionStartToken;
// add compile time error
kernelLibraryBuilder.addCompileTimeError(token.assertionMessage,
token.charOffset, token.endOffset - token.charOffset, debugExprUri);
}
var functionLastToken = startToken;
while (!functionLastToken.next.isEof) {
functionLastToken.offset = -1;
functionLastToken = functionLastToken.next;
}
functionLastToken.offset = -1;
functionLastToken.next = expressionStartToken;
expressionStartToken.previous = functionLastToken;
// If we're in a library loaded from a dill file, we'll have to do some
// extra work to get the scope setup.
if (enclosingLibraryBuilder is DillLibraryBuilder) {
dillLoadedData.loader.buildOutline(enclosingLibraryBuilder);
Map<Uri, LibraryBuilder> libraries = <Uri, LibraryBuilder>{};
if (userBuilders != null) libraries.addAll(userBuilders);
libraries.addAll(platformBuilders);
enclosingLibraryBuilder.addImportsToScope(libraries);
}
Scope scope =
inClass ? classBuilder.scope : enclosingLibraryBuilder.scope;
// Create a [ProcedureBuilder] and a [BodyBuilder] to parse the expression.
dynamicType() => new BuiltTypeBuilder(new DynamicType());
List<KernelFormalParameterBuilder> formalParameterBuilders =
<KernelFormalParameterBuilder>[];
definitions.forEach((name, type) {
formalParameterBuilders.add(new KernelFormalParameterBuilder(
null,
Modifier.varMask,
new BuiltTypeBuilder(type),
name,
false,
kernelLibraryBuilder,
-1));
});
List<KernelTypeVariableBuilder> typeVariableBuilders =
<KernelTypeVariableBuilder>[];
typeDefinitions.forEach((TypeParameter typeParam) {
typeVariableBuilders.add(new KernelTypeVariableBuilder(
typeParam.name,
kernelLibraryBuilder,
-1,
new BuiltTypeBuilder(typeParam.bound),
typeParam));
});
KernelProcedureBuilder procedureBuilder = new KernelProcedureBuilder(
/*metadata=*/ null,
isStatic ? Modifier.staticMask : Modifier.varMask,
dynamicType(),
"debugExpr",
typeVariableBuilders,
formalParameterBuilders,
ProcedureKind.Method,
kernelLibraryBuilder,
/*charOffset=*/ -1,
/*charOpenParenOffset=*/ -1,
/*charEndOffset=*/ -1);
Procedure procedure = procedureBuilder.build(kernelLibraryBuilder);
procedure.parent =
inClass ? classBuilder.target : kernelLibraryBuilder.target;
scope = procedureBuilder.computeTypeParameterScope(scope);
Scope formalParamScope =
procedureBuilder.computeFormalParameterScope(scope);
var typeInferenceEngine = new ShadowTypeInferenceEngine(
null, /*strongMode=*/ context.options.strongMode);
typeInferenceEngine.prepareTopLevel(
userCode.loader.coreTypes, userCode.loader.hierarchy);
InterfaceType thisType;
if (inClass) {
thisType = classBuilder.target.thisType;
}
TypeInferrer typeInferrer = typeInferenceEngine.createLocalTypeInferrer(
debugExprUri, thisType, kernelLibraryBuilder);
BodyBuilder bodyBuilder = new BodyBuilder(
kernelLibraryBuilder,
procedureBuilder,
scope,
formalParamScope,
userCode.loader.hierarchy,
userCode.loader.coreTypes,
classBuilder,
inClass && !isStatic,
null /*uri*/,
typeInferrer);
bodyBuilder.scope = formalParamScope;
// Parse the expression.
MemberKind kind = inClass
? (isStatic ? MemberKind.StaticMethod : MemberKind.NonStaticMethod)
: MemberKind.TopLevelMethod;
Parser parser = new Parser(bodyBuilder);
Token token = parser.syntheticPreviousToken(startToken);
token = parser.parseFormalParametersOpt(token, kind);
var formals = bodyBuilder.pop();
bodyBuilder.checkEmpty(token.next.charOffset);
parser.parseFunctionBody(
token, /*isExpression=*/ true, /*allowAbstract=*/ false);
var body = bodyBuilder.pop();
bodyBuilder.checkEmpty(token.charOffset);
bodyBuilder.finishFunction([], formals, AsyncMarker.Sync, body);
return procedure;
});
}
List<LibraryBuilder> computeReusedLibraries(
Set<Uri> invalidatedUris, UriTranslator uriTranslator) {
if (userCode == null && userBuilders == null) {

View file

@ -111,8 +111,11 @@ class KernelLibraryBuilder
/// the error message is the corresponding value in the map.
Map<String, String> unserializableExports;
KernelLibraryBuilder(Uri uri, Uri fileUri, Loader loader, this.actualOrigin)
: library = actualOrigin?.library ?? new Library(uri, fileUri: fileUri),
KernelLibraryBuilder(Uri uri, Uri fileUri, Loader loader, this.actualOrigin,
[Library intendedTarget])
: library = intendedTarget ??
actualOrigin?.library ??
new Library(uri, fileUri: fileUri),
super(loader, fileUri);
@override

View file

@ -29,9 +29,12 @@ class KernelTypeVariableBuilder
KernelTypeVariableBuilder(
String name, KernelLibraryBuilder compilationUnit, int charOffset,
[KernelTypeBuilder bound])
: actualParameter = new TypeParameter(name, null)
..fileOffset = charOffset,
[KernelTypeBuilder bound, TypeParameter actual])
// TODO(32378): We would like to use '??' here instead, but in conjuction
// with '..', it crashes Dart2JS.
: actualParameter = actual != null
? (actual..fileOffset = charOffset)
: (new TypeParameter(name, null)..fileOffset = charOffset),
super(name, bound, compilationUnit, charOffset);
@override

View file

@ -4,7 +4,7 @@
library dart2js.scanner.string_scanner;
import '../../scanner/token.dart' show SyntheticStringToken, TokenType;
import '../../scanner/token.dart' show SyntheticStringToken, Token, TokenType;
import '../../scanner/token.dart' as analyzer show StringToken;
@ -12,6 +12,8 @@ import 'array_based_scanner.dart' show ArrayBasedScanner;
import 'token.dart' show CommentToken, DartDocToken, StringToken;
import 'error_token.dart' show ErrorToken;
/**
* Scanner that reads from a String and creates tokens that points to
* substrings.
@ -37,6 +39,12 @@ class StringScanner extends ArrayBasedScanner {
: string;
}
static bool isLegalIdentifier(String identifier) {
StringScanner scanner = new StringScanner(identifier);
Token startToken = scanner.tokenize();
return startToken is! ErrorToken && startToken.next.isEof;
}
int advance() => string.codeUnitAt(++scanOffset);
int peek() => string.codeUnitAt(scanOffset + 1);

View file

@ -14,7 +14,6 @@ import "package:kernel/ast.dart"
show
Procedure,
Component,
CanonicalName,
DynamicType,
DartType,
TypeParameter;
@ -42,7 +41,7 @@ import 'package:front_end/src/base/processed_options.dart'
import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
import 'package:front_end/src/fasta/incremental_compiler.dart'
show IncrementalCompiler;
show IncrementalCompiler, IncrementalCompilationPosition;
import 'package:kernel/text/ast_to_text.dart' show Printer;
@ -123,7 +122,9 @@ class TestCase {
final bool isStaticMethod;
final CanonicalName enclosingNode;
final Uri library;
final String className;
String expression;
@ -135,7 +136,8 @@ class TestCase {
this.definitions,
this.typeDefinitions,
this.isStaticMethod,
this.enclosingNode,
this.library,
this.className,
this.expression);
String toString() {
@ -143,7 +145,8 @@ class TestCase {
"$entryPoint, "
"$definitions, "
"$typeDefinitions,"
"$enclosingNode, "
"$library, "
"$className, "
"static = $isStaticMethod)";
}
@ -155,7 +158,7 @@ class TestCase {
if (!(new File.fromUri(entryPoint)).existsSync()) {
return "Entry point $entryPoint doesn't exist.";
}
if (enclosingNode == null || enclosingNode == "") {
if (library == null) {
return "No enclosing node.";
}
if (expression == null) {
@ -233,7 +236,8 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
List<String> definitions = <String>[];
List<String> typeDefinitions = <String>[];
bool isStaticMethod = false;
CanonicalName enclosingNode;
Uri library;
String className;
String expression;
dynamic maps = loadYamlNode(contents, sourceUrl: uri);
@ -248,13 +252,10 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
if (key == "entry_point") {
entryPoint = description.uri.resolveUri(Uri.parse(value as String));
} else if (key == "position") {
Uri positionUri =
description.uri.resolveUri(Uri.parse(value as String));
enclosingNode = new CanonicalName.root();
enclosingNode =
enclosingNode.getChild("${positionUri.removeFragment()}");
if (positionUri.fragment != null && positionUri.fragment != '') {
enclosingNode = enclosingNode.getChild(positionUri.fragment);
Uri uri = description.uri.resolveUri(Uri.parse(value as String));
library = uri.removeFragment();
if (uri.fragment != null && uri.fragment != '') {
className = uri.fragment;
}
} else if (key == "definitions") {
definitions = (value as YamlList).map((x) => x as String).toList();
@ -268,7 +269,7 @@ class ReadTest extends Step<TestDescription, List<TestCase>, Context> {
}
}
var test = new TestCase(description, entryPoint, definitions,
typeDefinitions, isStaticMethod, enclosingNode, expression);
typeDefinitions, isStaticMethod, library, className, expression);
var result = test.validate();
if (result != null) {
return new Result.fail(tests, result);
@ -325,9 +326,15 @@ class CompileExpression extends Step<List<TestCase>, List<TestCase>, Context> {
}
for (var compiler in [sourceCompiler, dillCompiler]) {
// TODO: actually run the compiler
test.results
.add(new CompilationResult(compiler == null ? null : null, []));
IncrementalCompilationPosition enclosingNode =
compiler.resolveCompilationPosition(test.library, test.className);
Procedure compiledProcedure;
if (enclosingNode != null) {
compiledProcedure = await compiler.compileExpression(test.expression,
definitions, typeParams, enclosingNode, test.isStaticMethod);
}
var errors = context.takeErrors();
test.results.add(new CompilationResult(compiledProcedure, errors));
}
}
return new Result.pass(tests);
@ -357,7 +364,7 @@ Future<Context> createContext(
final List<CompilationMessage> errors = <CompilationMessage>[];
final CompilerOptions optionBuilder = new CompilerOptions()
..strongMode = false
..strongMode = true
..reportMessages = true
..verbose = true
..fileSystem = fs

View file

@ -49,6 +49,7 @@ final subpackageRules = {
'lib/src/base',
'lib/src/byte_store',
'lib/src/fasta',
'lib/src/fasta/scanner'
]),
'lib/src/api_unstable': new SubpackageRules(allowedDependencies: [
'lib/src',
@ -75,6 +76,7 @@ final subpackageRules = {
'lib/src/fasta/scanner',
'lib/src/fasta/source',
'lib/src/fasta/util',
'lib/src/fasta/type_inference',
'lib/src/scanner',
]),
'lib/src/fasta/builder': new SubpackageRules(allowedDependencies: [

View file

@ -1,47 +1,3 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class_capture.expression: Fail
class_getter.expression: Fail
class_invalid_static.expression: Fail
class_invalid_static_capture.expression: Fail
class_invalid_static_getter.expression: Fail
class_invalid_static_setter.expression: Fail
class_method.expression: Fail
class_setter.expression: Fail
class_static.expression: Fail
class_static2.expression: Fail
class_static3.expression: Fail
class_type_param_bound.expression: Fail
class_type_param_bound_illegal.expression: Fail
class_type_param_reference.expression: Fail
class_type_param_reference_arg.expression: Fail
class_type_param_reference_arg_inferred.expression: Fail
class_type_param_reference_ctor.expression: Fail
class_type_param_reference_ctor_inferred.expression: Fail
class_type_param_reference_var.expression: Fail
core_lib_imported.expression: Fail
core_lib_internal.expression: Fail
eval.dart: Fail
invalid.expression: Fail
lib_ctor.expression: Fail
lib_external_ctor.expression: Fail
lib_nonctor.expression: Fail
lib_nonreference.expression: Fail
lib_nonshown_ctor.expression: Fail
lib_reference.expression: Fail
lib_simple.expression: Fail
missing_variable_types.expression: Fail
param_assign.expression: Fail
param_capture.expression: Fail
param_conflict.expression: Fail
param_conflict_class.expression: Fail
param_method.expression: Fail
type_param_bound.expression: Fail
type_param_shadow.expression: Fail
type_param_shadow_arg.expression: Fail
type_param_shadow_arg_ctor_inferred.expression: Fail
type_param_shadow_arg_inferred.expression: Fail
type_param_shadow_ctor.expression: Fail
type_param_shadow_var.expression: Fail

View file

@ -2,10 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library main;
import 'dart:io' show File, Process, exit;

View file

@ -3,5 +3,5 @@ Errors: {
}
method /* from org-dartlang-debug:synthetic_debug_expression */ debugExpr<T extends dynamic>() → dynamic
return () → dart.core::Null {
main::A::debugExpr::T k = let final dynamic #t1 = new main::A::•<dynamic>() in let dynamic _ = null in invalid-expression "Error: A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'main::A::debugExpr::T'.\nTry changing the type of the left hand side, or casting the right hand side to 'main::A::debugExpr::T'.";
main::A::debugExpr::T k = let final dynamic #t1 = let dynamic _ = null in invalid-expression "Error: A value of type 'main::A<dynamic>' can't be assigned to a variable of type 'main::A::debugExpr::T'.\nTry changing the type of the left hand side, or casting the right hand side to 'main::A::debugExpr::T'." in let final dynamic #t2 = new main::A::•<dynamic>() in null;
};