Skip to content

Commit

Permalink
Type aliased inference of generative constructor invocation.
Browse files Browse the repository at this point in the history
This CL adds some infrastructure that makes it possible to defer the
inference of type arguments for a type alias which is used to specify
an instance creation:

  class B<X, Y> {}
  class C<X> implements B<String, X> {}
  typedef T<X> = C<List<X>>;

  void main() {
    B<Object, Iterable<num>> c = T(); // Infer `T<num>()`.
  }

The CL contains an implementation for type inference in the case
where the target is a non-redirecting constructor.

There is not yet an implementation of type inference for the case
where the type alias ultimately resolves to a redirecting factory
constructor, and the shadow nodes are eliminated as null.

Change-Id: I9721b293dce37313e046a8339359e51c2d54b4c3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150301
Commit-Queue: Erik Ernst <eernst@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
  • Loading branch information
eernstg authored and commit-bot@chromium.org committed Jun 23, 2020
1 parent 2451ff7 commit 5a6c1e5
Show file tree
Hide file tree
Showing 17 changed files with 535 additions and 27 deletions.
182 changes: 155 additions & 27 deletions pkg/front_end/lib/src/fasta/kernel/body_builder.dart
Expand Up @@ -317,6 +317,16 @@ class BodyBuilder extends ScopeListener<JumpTarget>
/// invocations are to be resolved in a separate step.
final List<Expression> redirectingFactoryInvocations = <Expression>[];

/// List of built type aliased generative constructor invocations that
/// require unaliasing.
final List<ConstructorInvocation> typeAliasedConstructorInvocations =
<ConstructorInvocation>[];

/// List of built type aliased factory constructor invocations that require
/// unaliasing.
final List<StaticInvocation> typeAliasedFactoryInvocations =
<StaticInvocation>[];

/// Variables with metadata. Their types need to be inferred late, for
/// example, in [finishFunction].
List<VariableDeclaration> variablesWithMetadata;
Expand Down Expand Up @@ -1129,7 +1139,14 @@ class BodyBuilder extends ScopeListener<JumpTarget>
return true;
}

// TODO(eernst): Rename this method now that it handles more tasks.
void resolveRedirectingFactoryTargets() {
_unaliasTypeAliasedConstructorInvocations();
_unaliasTypeAliasedFactoryInvocations();
_resolveRedirectingFactoryTargets();
}

void _resolveRedirectingFactoryTargets() {
for (StaticInvocation invocation in redirectingFactoryInvocations) {
// If the invocation was invalid, it or its parent has already been
// desugared into an exception throwing expression. There is nothing to
Expand Down Expand Up @@ -1221,6 +1238,25 @@ class BodyBuilder extends ScopeListener<JumpTarget>
redirectingFactoryInvocations.clear();
}

void _unaliasTypeAliasedConstructorInvocations() {
for (ConstructorInvocation invocation
in typeAliasedConstructorInvocations) {
// TODO(eernst): Should replace aliased constructor invocations,
// such that back ends don't see instance creations on type aliases.
invocation.replaceWith(new NullLiteral());
}
typeAliasedConstructorInvocations.clear();
}

void _unaliasTypeAliasedFactoryInvocations() {
for (StaticInvocation invocation in typeAliasedFactoryInvocations) {
// TODO(eernst): Should replace aliased factory invocations,
// such that back ends don't see instance creations on type aliases.
invocation.replaceWith(new NullLiteral());
}
typeAliasedFactoryInvocations.clear();
}

void finishVariableMetadata() {
List<VariableDeclaration> variablesWithMetadata =
this.variablesWithMetadata;
Expand Down Expand Up @@ -3907,6 +3943,7 @@ class BodyBuilder extends ScopeListener<JumpTarget>
@override
Expression buildStaticInvocation(Member target, Arguments arguments,
{Constness constness: Constness.implicit,
TypeAliasBuilder typeAliasBuilder,
int charOffset: -1,
int charLength: noLength}) {
// The argument checks for the initial target of redirecting factories
Expand Down Expand Up @@ -3935,11 +3972,21 @@ class BodyBuilder extends ScopeListener<JumpTarget>
return buildProblem(
fasta.messageNonConstConstructor, charOffset, charLength);
}
ConstructorInvocation node =
new ConstructorInvocation(target, arguments, isConst: isConst)
..fileOffset = charOffset;
libraryBuilder.checkBoundsInConstructorInvocation(
node, typeEnvironment, uri);
ConstructorInvocation node;
if (typeAliasBuilder == null) {
node = new ConstructorInvocation(target, arguments, isConst: isConst)
..fileOffset = charOffset;
libraryBuilder.checkBoundsInConstructorInvocation(
node, typeEnvironment, uri);
} else {
node = new TypeAliasedConstructorInvocationJudgment(
typeAliasBuilder, target, arguments,
isConst: isConst)
..fileOffset = charOffset;
// No type arguments were passed, so we need not check bounds.
assert(arguments.types.isEmpty);
typeAliasedConstructorInvocations.add(node);
}
return node;
} else {
Procedure procedure = target;
Expand All @@ -3952,12 +3999,22 @@ class BodyBuilder extends ScopeListener<JumpTarget>
return buildProblem(
fasta.messageNonConstFactory, charOffset, charLength);
}
StaticInvocation node = new FactoryConstructorInvocationJudgment(
target, arguments,
isConst: isConst)
..fileOffset = charOffset;
libraryBuilder.checkBoundsInFactoryInvocation(
node, typeEnvironment, uri);
StaticInvocation node;
if (typeAliasBuilder == null) {
node = new FactoryConstructorInvocationJudgment(target, arguments,
isConst: isConst)
..fileOffset = charOffset;
libraryBuilder.checkBoundsInFactoryInvocation(
node, typeEnvironment, uri);
} else {
node = new TypeAliasedFactoryInvocationJudgment(
typeAliasBuilder, target, arguments,
isConst: isConst)
..fileOffset = charOffset;
// No type arguments were passed, so we need not check bounds.
assert(arguments.types.isEmpty);
typeAliasedFactoryInvocations.add(node);
}
return node;
} else {
assert(constness == Constness.implicit);
Expand Down Expand Up @@ -4248,13 +4305,83 @@ class BodyBuilder extends ScopeListener<JumpTarget>
}
} else {
if (aliasBuilder.typeVariables?.isNotEmpty ?? false) {
// No type arguments provided to alias, but it is generic.
typeArgumentBuilders = new List<TypeBuilder>.filled(
aliasBuilder.typeVariables.length, null,
growable: true);
for (int i = 0; i < typeArgumentBuilders.length; ++i) {
// TODO(eernst): We must use inferred types, for now use defaults.
typeArgumentBuilders[i] = aliasBuilder.typeVariables[i].defaultType;
// Raw generic type alias used for instance creation, needs inference.
ClassBuilder classBuilder;
if (type is ClassBuilder) {
classBuilder = type;
} else {
if (type is InvalidTypeDeclarationBuilder) {
LocatedMessage message = type.message;
return evaluateArgumentsBefore(
arguments,
buildProblem(message.messageObject, nameToken.charOffset,
nameToken.lexeme.length));
} else {
errorName ??= debugName(type.fullNameForErrors, name);
}
errorName ??= name;

return throwNoSuchMethodError(forest.createNullLiteral(charOffset),
errorName, arguments, nameLastToken.charOffset,
message: message);
}
MemberBuilder b = classBuilder.findConstructorOrFactory(
name, charOffset, uri, libraryBuilder);
Member target = b?.member;
if (b == null) {
// Not found. Reported below.
} else if (b is AmbiguousMemberBuilder) {
message = b.message.withLocation(uri, charOffset, noLength);
} else if (b.isConstructor) {
if (classBuilder.isAbstract) {
return evaluateArgumentsBefore(
arguments,
buildAbstractClassInstantiationError(
fasta.templateAbstractClassInstantiation
.withArguments(type.name),
type.name,
nameToken.charOffset));
}
}
if (target is Constructor ||
(target is Procedure && target.kind == ProcedureKind.Factory)) {
Expression invocation;
invocation = buildStaticInvocation(target, arguments,
constness: constness,
typeAliasBuilder: aliasBuilder,
charOffset: nameToken.charOffset,
charLength: nameToken.length);
return invocation;
} else {
errorName ??= debugName(type.name, name);
return throwNoSuchMethodError(forest.createNullLiteral(charOffset),
errorName, arguments, nameLastToken.charOffset,
message: message);
}
} else {
// Empty `typeArguments` and `aliasBuilder``is non-generic, but it
// may still unalias to a class type with some type arguments.
if (type is ClassBuilder) {
List<TypeBuilder> unaliasedTypeArgumentBuilders =
aliasBuilder.unaliasTypeArguments(const []);
if (unaliasedTypeArgumentBuilders == null) {
// TODO(eernst): This is a wrong number of type arguments,
// occurring indirectly (in an alias of an alias, etc.).
return evaluateArgumentsBefore(
arguments,
buildProblem(
fasta.templateTypeArgumentMismatch
.withArguments(numberOfTypeParameters),
nameToken.charOffset,
nameToken.length,
suppressMessage: true));
}
List<DartType> dartTypeArguments = [];
for (TypeBuilder typeBuilder in unaliasedTypeArgumentBuilders) {
dartTypeArguments.add(typeBuilder.build(libraryBuilder));
}
assert(forest.argumentsTypeArguments(arguments).isEmpty);
forest.argumentsSetTypeArguments(arguments, dartTypeArguments);
}
}
}
Expand Down Expand Up @@ -4296,16 +4423,17 @@ class BodyBuilder extends ScopeListener<JumpTarget>
assert(forest.argumentsTypeArguments(arguments).isEmpty);
forest.argumentsSetTypeArguments(arguments, []);
} else {
// No type arguments provided to unaliased class, use defaults.
List<DartType> result = new List<DartType>.filled(
type.typeVariables.length, null,
growable: true);
for (int i = 0; i < result.length; ++i) {
result[i] =
type.typeVariables[i].defaultType?.build(type.library);
if (forest.argumentsTypeArguments(arguments).isEmpty) {
// No type arguments provided to unaliased class, use defaults.
List<DartType> result = new List<DartType>.filled(
type.typeVariables.length, null,
growable: true);
for (int i = 0; i < result.length; ++i) {
result[i] =
type.typeVariables[i].defaultType?.build(type.library);
}
forest.argumentsSetTypeArguments(arguments, result);
}
assert(forest.argumentsTypeArguments(arguments).isEmpty);
forest.argumentsSetTypeArguments(arguments, result);
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
Expand Up @@ -743,6 +743,41 @@ class InferenceVisitor
result.inferredType, result.applyResult(resultNode));
}

ExpressionInferenceResult visitTypeAliasedConstructorInvocationJudgment(
TypeAliasedConstructorInvocationJudgment node, DartType typeContext) {
assert(getExplicitTypeArguments(node.arguments) == null);
Typedef typedef = node.typeAliasBuilder.typedef;
FunctionType calleeType = node.target.function
.computeAliasedFunctionType(typedef, inferrer.library.library);
InvocationInferenceResult result = inferrer.inferInvocation(
typeContext, node.fileOffset, calleeType, node.arguments,
returnType: calleeType.returnType.unalias, isConst: node.isConst);
node.hasBeenInferred = true;
Expression resultNode = node;
if (!inferrer.isTopLevel) {
SourceLibraryBuilder library = inferrer.library;
library.checkBoundsInType(result.inferredType,
inferrer.typeSchemaEnvironment, inferrer.helper.uri, node.fileOffset,
inferred: true);
if (inferrer.isNonNullableByDefault) {
if (node.target == inferrer.coreTypes.listDefaultConstructor) {
resultNode = inferrer.helper.wrapInProblem(node,
messageDefaultListConstructorError, node.fileOffset, noLength);
}
}
}
return new ExpressionInferenceResult(
result.inferredType, result.applyResult(resultNode));
}

ExpressionInferenceResult visitTypeAliasedFactoryInvocationJudgment(
TypeAliasedFactoryInvocationJudgment node, DartType typeContext) {
// TODO(eernst): See visitTypeAliasedConstructorInvocationJudgment
// for an implementation handling a similar task.
throw "visitTypeAliasedFactoryInvocationJudgment: "
"${node.typeAliasBuilder}, ${node.target}, $typeContext";
}

@override
void visitFieldInitializer(FieldInitializer node) {
ExpressionInferenceResult initializerResult =
Expand Down
61 changes: 61 additions & 0 deletions pkg/front_end/lib/src/fasta/kernel/internal_ast.dart
Expand Up @@ -25,6 +25,8 @@ import 'package:kernel/text/ast_to_text.dart' show Precedence, Printer;
import 'package:kernel/src/printer.dart';
import 'package:kernel/core_types.dart';

import '../builder/type_alias_builder.dart';

import '../fasta_codes.dart'
show noLength, templateWebLiteralCannotBeRepresentedExactly;

Expand Down Expand Up @@ -704,6 +706,65 @@ class FactoryConstructorInvocationJudgment extends StaticInvocation
}
}

/// Shadow object for [ConstructorInvocation] when the procedure being invoked
/// is a type aliased constructor.
class TypeAliasedConstructorInvocationJudgment extends ConstructorInvocation
implements ExpressionJudgment {
bool hasBeenInferred = false;
final TypeAliasBuilder typeAliasBuilder;

TypeAliasedConstructorInvocationJudgment(
this.typeAliasBuilder, Constructor target, ArgumentsImpl arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);

@override
ExpressionInferenceResult acceptInference(
InferenceVisitor visitor, DartType typeContext) {
return visitor.visitTypeAliasedConstructorInvocationJudgment(
this, typeContext);
}

@override
String toString() {
return "TypeAliasedConstructorInvocationJudgment(${toStringInternal()})";
}

@override
String toStringInternal() {
return "";
}
}

/// Shadow object for [StaticInvocation] when the procedure being invoked is a
/// type aliased factory constructor.
class TypeAliasedFactoryInvocationJudgment extends StaticInvocation
implements ExpressionJudgment {
bool hasBeenInferred = false;
final TypeAliasBuilder typeAliasBuilder;

TypeAliasedFactoryInvocationJudgment(
this.typeAliasBuilder, Procedure target, ArgumentsImpl arguments,
{bool isConst: false})
: super(target, arguments, isConst: isConst);

@override
ExpressionInferenceResult acceptInference(
InferenceVisitor visitor, DartType typeContext) {
return visitor.visitTypeAliasedFactoryInvocationJudgment(this, typeContext);
}

@override
String toString() {
return "TypeAliasedConstructorInvocationJudgment(${toStringInternal()})";
}

@override
String toStringInternal() {
return "";
}
}

/// Front end specific implementation of [FunctionDeclaration].
class FunctionDeclarationImpl extends FunctionDeclaration {
bool hasImplicitReturnType = false;
Expand Down
3 changes: 3 additions & 0 deletions pkg/front_end/test/spell_checking_list_common.txt
Expand Up @@ -686,6 +686,7 @@ created
creates
creating
creation
creations
crossed
curly
current
Expand Down Expand Up @@ -1077,6 +1078,7 @@ experimentally
experiments
expired
explanation
explain
explicit
explicitly
exponent
Expand Down Expand Up @@ -2901,6 +2903,7 @@ targeted
targeting
targets
task
tasks
tear
tearing
technically
Expand Down

0 comments on commit 5a6c1e5

Please sign in to comment.