Skip to content

Commit a946459

Browse files
kallentuCommit Queue
authored andcommitted
[cfe] Dot shorthands: Constructor type parameters are inferred in the empty context.
Fixes https://github.com/dart-lang/co19/blob/master/LanguageFeatures/Static-access-shorthand/type_inference_A07_t01.dart and https://github.com/dart-lang/co19/blob/master/LanguageFeatures/Static-access-shorthand/type_inference_A07_t02.dart. This CL changes the following behaviour according to the spec -- In case of a constructor invocation, the shorthand expression is inferred in the empty context. Bug: #59758 Change-Id: Ia849888f1a810df7390a35e454a09f3fed458849 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417960 Commit-Queue: Kallen Tu <[email protected]> Reviewed-by: Chloe Stefantsova <[email protected]>
1 parent 474673f commit a946459

9 files changed

+325
-3
lines changed

pkg/front_end/lib/src/type_inference/inference_visitor.dart

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12166,9 +12166,23 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1216612166
node.name.text.length));
1216712167
}
1216812168

12169+
// With constructor invocations, the shorthand expression is preceded by
12170+
// the raw type and then type inference infers the type arguments.
12171+
FunctionType functionType = constructor.function
12172+
.computeThisFunctionType(Nullability.nonNullable);
12173+
InvocationInferenceResult result = inferInvocation(
12174+
this,
12175+
typeContext,
12176+
node.fileOffset,
12177+
new InvocationTargetFunctionType(functionType),
12178+
node.arguments as ArgumentsImpl,
12179+
isConst: node.isConst,
12180+
staticTarget: constructor);
1216912181
expr = new ConstructorInvocation(constructor, node.arguments,
1217012182
isConst: node.isConst)
1217112183
..fileOffset = node.fileOffset;
12184+
return new ExpressionInferenceResult(
12185+
result.inferredType, result.applyResult(expr));
1217212186
} else if (constructor is Procedure) {
1217312187
// [constructor] can be a [Procedure] if we have an extension type
1217412188
// constructor or a redirecting factory constructor.
@@ -12180,15 +12194,28 @@ class InferenceVisitorImpl extends InferenceVisitorBase
1218012194
node.name.text.length));
1218112195
}
1218212196

12197+
// With constructor invocations, the shorthand expression is preceded by
12198+
// the raw type and then type inference infers the type arguments.
12199+
FunctionType functionType = constructor.function
12200+
.computeThisFunctionType(Nullability.nonNullable);
12201+
InvocationInferenceResult result = inferInvocation(
12202+
this,
12203+
typeContext,
12204+
node.fileOffset,
12205+
new InvocationTargetFunctionType(functionType),
12206+
node.arguments as ArgumentsImpl,
12207+
isConst: node.isConst,
12208+
staticTarget: constructor);
1218312209
if (constructor.isRedirectingFactory) {
12184-
expr = new FactoryConstructorInvocation(constructor, node.arguments,
12185-
isConst: node.isConst)
12186-
..fileOffset = node.fileOffset;
12210+
expr = helper.resolveRedirectingFactoryTarget(
12211+
constructor, node.arguments, node.fileOffset, node.isConst)!;
1218712212
} else {
1218812213
expr = new StaticInvocation(constructor, node.arguments,
1218912214
isConst: node.isConst)
1219012215
..fileOffset = node.fileOffset;
1219112216
}
12217+
return new ExpressionInferenceResult(
12218+
result.inferredType, result.applyResult(expr));
1219212219
}
1219312220
}
1219412221

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2025, 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+
class C<T> {
6+
T value;
7+
C(this.value);
8+
C.id(this.value);
9+
10+
C<int> toInt(int v) => C<int>(v);
11+
}
12+
13+
extension type ET<T>(T v) {
14+
ET.id(this.v);
15+
16+
ET<int> toInt(int v) => ET<int>(v);
17+
}
18+
19+
class CC<T, S extends Iterable<T>> {
20+
T t;
21+
CC(this.t);
22+
}
23+
24+
U bar<U>(CC<U, Iterable<U>> cc) => cc.t;
25+
26+
main() {
27+
var list1 = [bar(.new("String"))];
28+
List<String> list2 = list1;
29+
30+
C<int> c1 = .new("String").toInt(1);
31+
C<int> c2 = .id("String").toInt(2);
32+
ET<int> et1 = .new("String").toInt(3);
33+
ET<int> et2 = .id("String").toInt(4);
34+
List<String> l =
35+
.generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList();
36+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C<T extends core::Object? = dynamic> extends core::Object {
6+
covariant-by-class field self::C::T% value;
7+
constructor •(self::C::T% value) → self::C<self::C::T%>
8+
: self::C::value = value, super core::Object::•()
9+
;
10+
constructor id(self::C::T% value) → self::C<self::C::T%>
11+
: self::C::value = value, super core::Object::•()
12+
;
13+
method toInt(core::int v) → self::C<core::int>
14+
return new self::C::•<core::int>(v);
15+
}
16+
class CC<T extends core::Object? = dynamic, S extends core::Iterable<self::CC::T%> = core::Iterable<dynamic>> extends core::Object {
17+
covariant-by-class field self::CC::T% t;
18+
constructor •(self::CC::T% t) → self::CC<self::CC::T%, self::CC::S>
19+
: self::CC::t = t, super core::Object::•()
20+
;
21+
}
22+
extension type ET<T extends core::Object? = dynamic>(T% v) {
23+
abstract extension-type-member representation-field get v() → T%;
24+
method toInt = self::ET|toInt;
25+
method tearoff toInt = self::ET|get#toInt;
26+
constructor • = self::ET|constructor#;
27+
constructor tearoff • = self::ET|constructor#_#new#tearOff;
28+
constructor id = self::ET|constructor#id;
29+
constructor tearoff id = self::ET|constructor#_#id#tearOff;
30+
}
31+
static extension-type-member method ET|constructor#<T extends core::Object? = dynamic>(self::ET|constructor#::T% v) → self::ET<self::ET|constructor#::T%>% /* erasure=self::ET|constructor#::T%, declared=! */ {
32+
lowered final self::ET<self::ET|constructor#::T%>% /* erasure=self::ET|constructor#::T%, declared=! */ #this = v;
33+
return #this;
34+
}
35+
static extension-type-member method ET|constructor#_#new#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#new#tearOff::T% v) → self::ET<self::ET|constructor#_#new#tearOff::T%>% /* erasure=self::ET|constructor#_#new#tearOff::T%, declared=! */
36+
return self::ET|constructor#<self::ET|constructor#_#new#tearOff::T%>(v);
37+
static extension-type-member method ET|constructor#id<T extends core::Object? = dynamic>(self::ET|constructor#id::T% v) → self::ET<self::ET|constructor#id::T%>% /* erasure=self::ET|constructor#id::T%, declared=! */ {
38+
lowered final self::ET<self::ET|constructor#id::T%>% /* erasure=self::ET|constructor#id::T%, declared=! */ #this = v;
39+
return #this;
40+
}
41+
static extension-type-member method ET|constructor#_#id#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#id#tearOff::T% v) → self::ET<self::ET|constructor#_#id#tearOff::T%>% /* erasure=self::ET|constructor#_#id#tearOff::T%, declared=! */
42+
return self::ET|constructor#id<self::ET|constructor#_#id#tearOff::T%>(v);
43+
static extension-type-member method ET|toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|toInt::T%>% /* erasure=self::ET|toInt::T%, declared=! */ #this, core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */
44+
return self::ET|constructor#<core::int>(v);
45+
static extension-type-member method ET|get#toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|get#toInt::T%>% /* erasure=self::ET|get#toInt::T%, declared=! */ #this) → (core::int) → self::ET<core::int>% /* erasure=core::int, declared=! */
46+
return (core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */ => self::ET|toInt<self::ET|get#toInt::T%>(#this, v);
47+
static method bar<U extends core::Object? = dynamic>(self::CC<self::bar::U%, core::Iterable<self::bar::U%>> cc) → self::bar::U%
48+
return cc.{self::CC::t}{self::bar::U%};
49+
static method main() → dynamic {
50+
core::List<core::String> list1 = <core::String>[self::bar<core::String>(new self::CC::•<core::String, core::Iterable<core::String>>("String"))];
51+
core::List<core::String> list2 = list1;
52+
self::C<core::int> c1 = new self::C::•<core::String>("String").{self::C::toInt}(1){(core::int) → self::C<core::int>};
53+
self::C<core::int> c2 = new self::C::id<core::String>("String").{self::C::toInt}(2){(core::int) → self::C<core::int>};
54+
self::ET<core::int>% /* erasure=core::int, declared=! */ et1 = self::ET|toInt<core::String>(self::ET|constructor#<core::String>("String"), 3);
55+
self::ET<core::int>% /* erasure=core::int, declared=! */ et2 = self::ET|toInt<core::String>(self::ET|constructor#id<core::String>("String"), 4);
56+
core::List<core::String> l = core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::+}(1){(core::num) → core::int}).{core::Iterable::map}<core::String>((core::int x) → core::String => x.{core::int::toRadixString}(16){(core::int) → core::String}){((core::int) → core::String) → core::Iterable<core::String>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<core::String>};
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C<T extends core::Object? = dynamic> extends core::Object {
6+
covariant-by-class field self::C::T% value;
7+
constructor •(self::C::T% value) → self::C<self::C::T%>
8+
: self::C::value = value, super core::Object::•()
9+
;
10+
constructor id(self::C::T% value) → self::C<self::C::T%>
11+
: self::C::value = value, super core::Object::•()
12+
;
13+
method toInt(core::int v) → self::C<core::int>
14+
return new self::C::•<core::int>(v);
15+
}
16+
class CC<T extends core::Object? = dynamic, S extends core::Iterable<self::CC::T%> = core::Iterable<dynamic>> extends core::Object {
17+
covariant-by-class field self::CC::T% t;
18+
constructor •(self::CC::T% t) → self::CC<self::CC::T%, self::CC::S>
19+
: self::CC::t = t, super core::Object::•()
20+
;
21+
}
22+
extension type ET<T extends core::Object? = dynamic>(T% v) {
23+
abstract extension-type-member representation-field get v() → T%;
24+
method toInt = self::ET|toInt;
25+
method tearoff toInt = self::ET|get#toInt;
26+
constructor • = self::ET|constructor#;
27+
constructor tearoff • = self::ET|constructor#_#new#tearOff;
28+
constructor id = self::ET|constructor#id;
29+
constructor tearoff id = self::ET|constructor#_#id#tearOff;
30+
}
31+
static extension-type-member method ET|constructor#<T extends core::Object? = dynamic>(self::ET|constructor#::T% v) → self::ET<self::ET|constructor#::T%>% /* erasure=self::ET|constructor#::T%, declared=! */ {
32+
lowered final self::ET<self::ET|constructor#::T%>% /* erasure=self::ET|constructor#::T%, declared=! */ #this = v;
33+
return #this;
34+
}
35+
static extension-type-member method ET|constructor#_#new#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#new#tearOff::T% v) → self::ET<self::ET|constructor#_#new#tearOff::T%>% /* erasure=self::ET|constructor#_#new#tearOff::T%, declared=! */
36+
return self::ET|constructor#<self::ET|constructor#_#new#tearOff::T%>(v);
37+
static extension-type-member method ET|constructor#id<T extends core::Object? = dynamic>(self::ET|constructor#id::T% v) → self::ET<self::ET|constructor#id::T%>% /* erasure=self::ET|constructor#id::T%, declared=! */ {
38+
lowered final self::ET<self::ET|constructor#id::T%>% /* erasure=self::ET|constructor#id::T%, declared=! */ #this = v;
39+
return #this;
40+
}
41+
static extension-type-member method ET|constructor#_#id#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#id#tearOff::T% v) → self::ET<self::ET|constructor#_#id#tearOff::T%>% /* erasure=self::ET|constructor#_#id#tearOff::T%, declared=! */
42+
return self::ET|constructor#id<self::ET|constructor#_#id#tearOff::T%>(v);
43+
static extension-type-member method ET|toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|toInt::T%>% /* erasure=self::ET|toInt::T%, declared=! */ #this, core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */
44+
return self::ET|constructor#<core::int>(v);
45+
static extension-type-member method ET|get#toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|get#toInt::T%>% /* erasure=self::ET|get#toInt::T%, declared=! */ #this) → (core::int) → self::ET<core::int>% /* erasure=core::int, declared=! */
46+
return (core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */ => self::ET|toInt<self::ET|get#toInt::T%>(#this, v);
47+
static method bar<U extends core::Object? = dynamic>(self::CC<self::bar::U%, core::Iterable<self::bar::U%>> cc) → self::bar::U%
48+
return cc.{self::CC::t}{self::bar::U%};
49+
static method main() → dynamic {
50+
core::List<core::String> list1 = <core::String>[self::bar<core::String>(new self::CC::•<core::String, core::Iterable<core::String>>("String"))];
51+
core::List<core::String> list2 = list1;
52+
self::C<core::int> c1 = new self::C::•<core::String>("String").{self::C::toInt}(1){(core::int) → self::C<core::int>};
53+
self::C<core::int> c2 = new self::C::id<core::String>("String").{self::C::toInt}(2){(core::int) → self::C<core::int>};
54+
self::ET<core::int>% /* erasure=core::int, declared=! */ et1 = self::ET|toInt<core::String>(self::ET|constructor#<core::String>("String"), 3);
55+
self::ET<core::int>% /* erasure=core::int, declared=! */ et2 = self::ET|toInt<core::String>(self::ET|constructor#id<core::String>("String"), 4);
56+
core::List<core::String> l = core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::+}(1){(core::num) → core::int}).{core::Iterable::map}<core::String>((core::int x) → core::String => x.{core::int::toRadixString}(16){(core::int) → core::String}){((core::int) → core::String) → core::Iterable<core::String>}.{core::Iterable::toList}(){({growable: core::bool}) → core::List<core::String>};
57+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C<T extends core::Object? = dynamic> extends core::Object {
6+
covariant-by-class field self::C::T% value;
7+
constructor •(self::C::T% value) → self::C<self::C::T%>
8+
;
9+
constructor id(self::C::T% value) → self::C<self::C::T%>
10+
;
11+
method toInt(core::int v) → self::C<core::int>
12+
;
13+
}
14+
class CC<T extends core::Object? = dynamic, S extends core::Iterable<self::CC::T%> = core::Iterable<dynamic>> extends core::Object {
15+
covariant-by-class field self::CC::T% t;
16+
constructor •(self::CC::T% t) → self::CC<self::CC::T%, self::CC::S>
17+
;
18+
}
19+
extension type ET<T extends core::Object? = dynamic>(T% v) {
20+
abstract extension-type-member representation-field get v() → T%;
21+
method toInt = self::ET|toInt;
22+
method tearoff toInt = self::ET|get#toInt;
23+
constructor • = self::ET|constructor#;
24+
constructor tearoff • = self::ET|constructor#_#new#tearOff;
25+
constructor id = self::ET|constructor#id;
26+
constructor tearoff id = self::ET|constructor#_#id#tearOff;
27+
}
28+
static extension-type-member method ET|constructor#<T extends core::Object? = dynamic>(self::ET|constructor#::T% v) → self::ET<self::ET|constructor#::T%>% /* erasure=self::ET|constructor#::T%, declared=! */
29+
;
30+
static extension-type-member method ET|constructor#_#new#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#new#tearOff::T% v) → self::ET<self::ET|constructor#_#new#tearOff::T%>% /* erasure=self::ET|constructor#_#new#tearOff::T%, declared=! */
31+
return self::ET|constructor#<self::ET|constructor#_#new#tearOff::T%>(v);
32+
static extension-type-member method ET|constructor#id<T extends core::Object? = dynamic>(self::ET|constructor#id::T% v) → self::ET<self::ET|constructor#id::T%>% /* erasure=self::ET|constructor#id::T%, declared=! */
33+
;
34+
static extension-type-member method ET|constructor#_#id#tearOff<T extends core::Object? = dynamic>(self::ET|constructor#_#id#tearOff::T% v) → self::ET<self::ET|constructor#_#id#tearOff::T%>% /* erasure=self::ET|constructor#_#id#tearOff::T%, declared=! */
35+
return self::ET|constructor#id<self::ET|constructor#_#id#tearOff::T%>(v);
36+
static extension-type-member method ET|toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|toInt::T%>% /* erasure=self::ET|toInt::T%, declared=! */ #this, core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */
37+
;
38+
static extension-type-member method ET|get#toInt<T extends core::Object? = dynamic>(lowered final self::ET<self::ET|get#toInt::T%>% /* erasure=self::ET|get#toInt::T%, declared=! */ #this) → (core::int) → self::ET<core::int>% /* erasure=core::int, declared=! */
39+
return (core::int v) → self::ET<core::int>% /* erasure=core::int, declared=! */ => self::ET|toInt<self::ET|get#toInt::T%>(#this, v);
40+
static method bar<U extends core::Object? = dynamic>(self::CC<self::bar::U%, core::Iterable<self::bar::U%>> cc) → self::bar::U%
41+
;
42+
static method main() → dynamic
43+
;

0 commit comments

Comments
 (0)