mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 17:59:39 +00:00
[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:
parent
d18893c799
commit
8cc76bde6e
|
@ -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,
|
||||
|
|
|
@ -1555,7 +1555,7 @@ class StaticAccessGenerator extends Generator {
|
|||
arguments);
|
||||
} else {
|
||||
return _helper.buildStaticInvocation(readTarget as Procedure, arguments,
|
||||
charOffset: offset);
|
||||
charOffset: offset, isConstructorInvocation: false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
14
pkg/front_end/testcases/inline_class/issue51146.dart
Normal file
14
pkg/front_end/testcases/inline_class/issue51146.dart
Normal 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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
inline class I<X, Y> {
|
||||
final X value;
|
||||
I(this.value);
|
||||
}
|
||||
|
||||
void f(I<int, String> i) {}
|
||||
void main() {}
|
|
@ -0,0 +1,7 @@
|
|||
inline class I<X, Y> {
|
||||
I(this.value);
|
||||
final X value;
|
||||
}
|
||||
|
||||
void f(I<int, String> i) {}
|
||||
void main() {}
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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
|
||||
;
|
|
@ -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));
|
||||
}
|
Loading…
Reference in a new issue