[cfe] Support type inference on inline class constructor invocation

Closes #51146

Change-Id: I8fab2e999b29fbc811786e168d0e7a4c5a081418
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/295363
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2023-04-24 10:17:54 +00:00 committed by Commit Queue
parent d18893c799
commit 8cc76bde6e
21 changed files with 191 additions and 21 deletions

View file

@ -1567,7 +1567,8 @@ class BodyBuilder extends StackListenerImpl
named: arguments.named,
hasExplicitTypeArguments: hasExplicitTypeArguments(arguments)),
constness: isConst ? Constness.explicitConst : Constness.explicitNew,
charOffset: fileOffset);
charOffset: fileOffset,
isConstructorInvocation: true);
}
return replacementNode;
}
@ -5970,7 +5971,8 @@ class BodyBuilder extends StackListenerImpl
{Constness constness = Constness.implicit,
TypeAliasBuilder? typeAliasBuilder,
int charOffset = -1,
int charLength = noLength}) {
int charLength = noLength,
required bool isConstructorInvocation}) {
// The argument checks for the initial target of redirecting factories
// invocations are skipped in Dart 1.
List<TypeParameter> typeParameters = target.function!.typeParameters;
@ -6018,7 +6020,7 @@ class BodyBuilder extends StackListenerImpl
return node;
} else {
Procedure procedure = target as Procedure;
if (procedure.isFactory) {
if (isConstructorInvocation) {
if (constantContext == ConstantContext.required &&
constness == Constness.implicit) {
addProblem(fasta.messageMissingExplicitConst, charOffset, charLength);
@ -6051,8 +6053,7 @@ class BodyBuilder extends StackListenerImpl
}
return node;
} else {
assert(
constness == Constness.implicit || procedure.isInlineClassMember);
assert(constness == Constness.implicit);
return new StaticInvocation(target, arguments, isConst: false)
..fileOffset = charOffset;
}
@ -6494,7 +6495,8 @@ class BodyBuilder extends StackListenerImpl
constness: constness,
typeAliasBuilder: aliasBuilder,
charOffset: nameToken.charOffset,
charLength: nameToken.length);
charLength: nameToken.length,
isConstructorInvocation: true);
return invocation;
} else {
return buildUnresolvedError(errorName, nameLastToken.charOffset,
@ -6647,7 +6649,8 @@ class BodyBuilder extends StackListenerImpl
constness: constness,
charOffset: nameToken.charOffset,
charLength: nameToken.length,
typeAliasBuilder: typeAliasBuilder as TypeAliasBuilder?);
typeAliasBuilder: typeAliasBuilder as TypeAliasBuilder?,
isConstructorInvocation: true);
return invocation;
} else {
errorName ??= debugName(type.name, name);
@ -6668,7 +6671,8 @@ class BodyBuilder extends StackListenerImpl
constness: constness,
charOffset: nameToken.charOffset,
charLength: nameToken.length,
typeAliasBuilder: typeAliasBuilder as TypeAliasBuilder?);
typeAliasBuilder: typeAliasBuilder as TypeAliasBuilder?,
isConstructorInvocation: true);
} else {
errorName ??= debugName(type.name, name);
}
@ -8899,7 +8903,8 @@ class BodyBuilder extends StackListenerImpl
forest.createStringLiteral(assignmentOffset, name)
]),
constness: Constness.explicitNew,
charOffset: assignmentOffset);
charOffset: assignmentOffset,
isConstructorInvocation: true);
return <Initializer>[
new ShadowInvalidFieldInitializer(
builder.field,

View file

@ -1555,7 +1555,7 @@ class StaticAccessGenerator extends Generator {
arguments);
} else {
return _helper.buildStaticInvocation(readTarget as Procedure, arguments,
charOffset: offset);
charOffset: offset, isConstructorInvocation: false);
}
}

View file

@ -85,7 +85,8 @@ abstract class ExpressionGeneratorHelper implements InferenceHelper {
Expression buildStaticInvocation(Member target, Arguments arguments,
{Constness constness = Constness.implicit,
int charOffset = TreeNode.noOffset});
int charOffset = TreeNode.noOffset,
required bool isConstructorInvocation});
Expression buildExtensionMethodInvocation(
int fileOffset, Procedure target, Arguments arguments,

View file

@ -88,6 +88,12 @@ class TypeBuilderConstraintGatherer extends TypeConstraintGatherer
return hierarchy.getTypeArgumentsAsInstanceOf(type, superclass);
}
@override
List<DartType>? getInlineTypeArgumentsAsInstanceOf(
InlineType type, InlineClass superclass) {
return hierarchy.getInlineTypeArgumentsAsInstanceOf(type, superclass);
}
@override
InterfaceType futureType(DartType type, Nullability nullability) {
return new InterfaceType(

View file

@ -771,7 +771,8 @@ class SourceEnumBuilder extends SourceClassBuilder {
Expression initializer = bodyBuilder.buildStaticInvocation(
constructorBuilder.invokeTarget, arguments,
constness: Constness.explicitConst,
charOffset: fieldBuilder.charOffset);
charOffset: fieldBuilder.charOffset,
isConstructorInvocation: true);
ExpressionInferenceResult inferenceResult = bodyBuilder.typeInferrer
.inferFieldInitializer(
bodyBuilder, const UnknownType(), initializer);

View file

@ -4466,10 +4466,10 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
{bool inferred = false}) {
if (node.arguments.types.isEmpty) return;
Procedure factory = node.target;
assert(factory.isFactory);
Class klass = factory.enclosingClass!;
DartType constructedType = new InterfaceType(
klass, klass.enclosingLibrary.nonNullable, node.arguments.types);
assert(factory.isFactory || factory.isInlineClassMember);
DartType constructedType = Substitution.fromPairs(
node.target.function.typeParameters, node.arguments.types)
.substituteType(node.target.function.returnType);
checkBoundsInType(
constructedType, typeEnvironment, fileUri, node.fileOffset,
inferred: inferred, allowSuperBounded: false);

View file

@ -67,6 +67,9 @@ abstract class TypeConstraintGatherer {
List<DartType>? getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass);
List<DartType>? getInlineTypeArgumentsAsInstanceOf(
InlineType type, InlineClass superclass);
InterfaceType futureType(DartType type, Nullability nullability);
/// Returns the set of type constraints that was gathered.
@ -635,6 +638,21 @@ abstract class TypeConstraintGatherer {
p.classNode == q.classNode) {
assert(p.typeArguments.length == q.typeArguments.length);
final int baseConstraintCount = _protoConstraints.length;
bool isMatch = true;
for (int i = 0; isMatch && i < p.typeArguments.length; ++i) {
isMatch = isMatch &&
_isNullabilityAwareSubtypeMatch(
p.typeArguments[i], q.typeArguments[i],
constrainSupertype: constrainSupertype);
}
if (isMatch) return true;
_protoConstraints.length = baseConstraintCount;
} else if (p is InlineType &&
q is InlineType &&
p.inlineClass == q.inlineClass) {
assert(p.typeArguments.length == q.typeArguments.length);
final int baseConstraintCount = _protoConstraints.length;
bool isMatch = true;
for (int i = 0; isMatch && i < p.typeArguments.length; ++i) {
@ -659,6 +677,22 @@ abstract class TypeConstraintGatherer {
if (sArguments != null) {
assert(sArguments.length == q.typeArguments.length);
final int baseConstraintCount = _protoConstraints.length;
bool isMatch = true;
for (int i = 0; isMatch && i < sArguments.length; ++i) {
isMatch = isMatch &&
_isNullabilityAwareSubtypeMatch(sArguments[i], q.typeArguments[i],
constrainSupertype: constrainSupertype);
}
if (isMatch) return true;
_protoConstraints.length = baseConstraintCount;
}
} else if (p is InlineType && q is InlineType) {
final List<DartType>? sArguments =
getInlineTypeArgumentsAsInstanceOf(p, q.inlineClass);
if (sArguments != null) {
assert(sArguments.length == q.typeArguments.length);
final int baseConstraintCount = _protoConstraints.length;
bool isMatch = true;
for (int i = 0; isMatch && i < sArguments.length; ++i) {
@ -1134,6 +1168,13 @@ class TypeSchemaConstraintGatherer extends TypeConstraintGatherer {
return environment.getTypeArgumentsAsInstanceOf(type, superclass);
}
@override
List<DartType>? getInlineTypeArgumentsAsInstanceOf(
InlineType type, InlineClass superclass) {
return environment.hierarchy
.getInlineTypeArgumentsAsInstanceOf(type, superclass);
}
@override
InterfaceType futureType(DartType type, Nullability nullability) {
return environment.futureType(type, nullability);

View file

@ -35,7 +35,7 @@ static method main() → dynamic {
self::GenericClass<core::String> e = self::GenericClass|<core::String>("bar");
self::GenericClass<core::int> f = self::GenericClass|<core::int>(42);
self::GenericClass<core::int> g = self::GenericClass|<core::int>(87);
self::GenericClass<core::num> h = self::GenericClass|<core::int>(123);
self::GenericClass<core::num> h = self::GenericClass|<core::num>(123);
self::expect(3, a as{ForNonNullableByDefault} core::int);
self::expect(3, a);
self::expect(4, b as{ForNonNullableByDefault} core::int);

View file

@ -35,7 +35,7 @@ static method main() → dynamic {
self::GenericClass<core::String> e = self::GenericClass|<core::String>("bar");
self::GenericClass<core::int> f = self::GenericClass|<core::int>(42);
self::GenericClass<core::int> g = self::GenericClass|<core::int>(87);
self::GenericClass<core::num> h = self::GenericClass|<core::int>(123);
self::GenericClass<core::num> h = self::GenericClass|<core::num>(123);
self::expect(3, a as{ForNonNullableByDefault} core::int);
self::expect(3, a);
self::expect(4, b as{ForNonNullableByDefault} core::int);

View file

@ -35,7 +35,7 @@ static method main() → dynamic {
self::GenericClass<core::String> e = self::GenericClass|<core::String>("bar");
self::GenericClass<core::int> f = self::GenericClass|<core::int>(42);
self::GenericClass<core::int> g = self::GenericClass|<core::int>(87);
self::GenericClass<core::num> h = self::GenericClass|<core::int>(123);
self::GenericClass<core::num> h = self::GenericClass|<core::num>(123);
self::expect(3, a as{ForNonNullableByDefault} core::int);
self::expect(3, a);
self::expect(4, b as{ForNonNullableByDefault} core::int);

View file

@ -35,7 +35,7 @@ static method main() → dynamic {
self::GenericClass<core::String> e = self::GenericClass|<core::String>("bar");
self::GenericClass<core::int> f = self::GenericClass|<core::int>(42);
self::GenericClass<core::int> g = self::GenericClass|<core::int>(87);
self::GenericClass<core::num> h = self::GenericClass|<core::int>(123);
self::GenericClass<core::num> h = self::GenericClass|<core::num>(123);
self::expect(3, a as{ForNonNullableByDefault} core::int);
self::expect(3, a);
self::expect(4, b as{ForNonNullableByDefault} core::int);

View file

@ -35,7 +35,7 @@ static method main() → dynamic {
self::GenericClass<core::String> e = self::GenericClass|<core::String>("bar");
self::GenericClass<core::int> f = self::GenericClass|<core::int>(42);
self::GenericClass<core::int> g = self::GenericClass|<core::int>(87);
self::GenericClass<core::num> h = self::GenericClass|<core::int>(123);
self::GenericClass<core::num> h = self::GenericClass|<core::num>(123);
self::expect(3, a as{ForNonNullableByDefault} core::int);
self::expect(3, a);
self::expect(4, b as{ForNonNullableByDefault} core::int);

View file

@ -0,0 +1,14 @@
// Copyright (c) 2023, 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.
inline class I<X, Y> {
final X value;
I(this.value);
}
void f(I<int, String> i) {}
void main() {
f(I(2));
}

View file

@ -0,0 +1,15 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%> {
final self::I<self::I|::X%, self::I|::Y%> #this = value;
return #this;
}
static method f(self::I<core::int, core::String> i) → void {}
static method main() → void {
self::f(self::I|<core::int, core::String>(2));
}

View file

@ -0,0 +1,15 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%> {
final self::I<self::I|::X%, self::I|::Y%> #this = value;
return #this;
}
static method f(self::I<core::int, core::String> i) → void {}
static method main() → void {
self::f(self::I|<core::int, core::String>(2));
}

View file

@ -0,0 +1,7 @@
inline class I<X, Y> {
final X value;
I(this.value);
}
void f(I<int, String> i) {}
void main() {}

View file

@ -0,0 +1,7 @@
inline class I<X, Y> {
I(this.value);
final X value;
}
void f(I<int, String> i) {}
void main() {}

View file

@ -0,0 +1,15 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%> {
final self::I<self::I|::X%, self::I|::Y%> #this = value;
return #this;
}
static method f(self::I<core::int, core::String> i) → void {}
static method main() → void {
self::f(self::I|<core::int, core::String>(2));
}

View file

@ -0,0 +1,15 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%> {
final self::I<self::I|::X%, self::I|::Y%> #this = value;
return #this;
}
static method f(self::I<core::int, core::String> i) → void {}
static method main() → void {
self::f(self::I|<core::int, core::String>(2));
}

View file

@ -0,0 +1,13 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%>
;
static method f(self::I<core::int, core::String> i) → void
;
static method main() → void
;

View file

@ -0,0 +1,15 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
inline class I<X extends core::Object? = dynamic, Y extends core::Object? = dynamic> /* declaredRepresentationType = X% */ {
constructor • = self::I|;
}
static method I|<X extends core::Object? = dynamic, Y extends core::Object? = dynamic>(self::I|::X% value) → self::I<self::I|::X%, self::I|::Y%> {
final self::I<self::I|::X%, self::I|::Y%> #this = value;
return #this;
}
static method f(self::I<core::int, core::String> i) → void {}
static method main() → void {
self::f(self::I|<core::int, core::String>(2));
}