Skip to content

Commit

Permalink
[cfe] Provide inferred type context to initializing formals
Browse files Browse the repository at this point in the history
In response to flutter/flutter#160551

Change-Id: I0e4123c76f8d82a6900e12452f0deef2cbb05d36
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404822
Reviewed-by: Johnni Winther <[email protected]>
Commit-Queue: Chloe Stefantsova <[email protected]>
  • Loading branch information
chloestefantsova authored and Commit Queue committed Jan 17, 2025
1 parent d2ce8e7 commit 39d2653
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 16 deletions.
42 changes: 26 additions & 16 deletions pkg/front_end/lib/src/source/source_factory_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,32 @@ class RedirectingFactoryBuilder extends SourceFactoryBuilder {
.createBodyBuilderForOutlineExpression(libraryBuilder,
createBodyBuilderContext(), declarationBuilder.scope, fileUri);
Builder? targetBuilder = redirectionTarget.target;

// Inference of target's formals should happen before building of the
// outline expressions in members and before inferring target's type
// arguments.
//
// (1) The outline expressions, such as formal parameter initializers,
// need properly inferred type contexts. Among other things, it ensures
// that the required coercions, such as int-to-double conversion, are
// run.
//
// (2) Type arguments for the targets of redirecting factories can only
// be inferred if the formal parameters of the targets are inferred too.
// That may not be the case when the target's parameters are initializing
// parameters referring to fields with types that are to be inferred.
if (targetBuilder is SourceFunctionBuilderImpl) {
List<FormalParameterBuilder>? formals = targetBuilder.formals;
if (formals != null) {
for (FormalParameterBuilder formal in formals) {
TypeBuilder formalType = formal.type;
if (formalType is InferableTypeBuilder) {
formalType.inferType(classHierarchy);
}
}
}
}

if (targetBuilder is SourceMemberBuilder) {
// Ensure that target has been built.
targetBuilder.buildOutlineExpressions(
Expand All @@ -598,22 +624,6 @@ class RedirectingFactoryBuilder extends SourceFactoryBuilder {
fileOffset, fileUri);
}

// Type arguments for the targets of redirecting factories can only be
// inferred if the formal parameters of the targets are inferred too.
// That may not be the case when the target's parameters are initializing
// parameters referring to fields with types that are to be inferred.
if (targetBuilder is SourceFunctionBuilderImpl) {
List<FormalParameterBuilder>? formals = targetBuilder.formals;
if (formals != null) {
for (FormalParameterBuilder formal in formals) {
TypeBuilder formalType = formal.type;
if (formalType is InferableTypeBuilder) {
formalType.inferType(classHierarchy);
}
}
}
}

typeArguments = inferrer.inferRedirectingFactoryTypeArguments(
helper,
_procedureInternal.function.returnType,
Expand Down
1 change: 1 addition & 0 deletions pkg/front_end/test/spell_checking_list_common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ coerced
coerces
coercing
coercion
coercions
coincides
coinductively
collapses
Expand Down
18 changes: 18 additions & 0 deletions pkg/front_end/testcases/inference/flutter_issue160551.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2025, 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.

void main() {
print(B().foo);
}

abstract class A {
A();

factory A.redir({double foo}) = B;
}

class B<T> extends A {
B({this.foo = 10});
final double foo;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
library;
import self as self;
import "dart:core" as core;

abstract class A extends core::Object {
constructor •() → self::A
: super core::Object::•()
;
static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
return new self::B::•<dynamic>(foo: foo);
}
class B<T extends core::Object? = dynamic> extends self::A {
final field core::double foo;
constructor •({core::double foo = #C1}) → self::B<self::B::T%>
: self::B::foo = foo, super self::A::•()
;
}
static method main() → void {
core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
}

constants {
#C1 = 10.0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
library;
import self as self;
import "dart:core" as core;

abstract class A extends core::Object {
constructor •() → self::A
: super core::Object::•()
;
static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
return new self::B::•<dynamic>(foo: foo);
}
class B<T extends core::Object? = dynamic> extends self::A {
final field core::double foo;
constructor •({core::double foo = #C1}) → self::B<self::B::T%>
: self::B::foo = foo, super self::A::•()
;
}
static method main() → void {
core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
}

constants {
#C1 = 10.0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
library;
import self as self;
import "dart:core" as core;

abstract class A extends core::Object {
constructor •() → self::A
;
static factory redir({core::double foo = null}) → self::A /* redirection-target: self::B::•<dynamic>*/
return new self::B::•<dynamic>(foo: foo);
}
class B<T extends core::Object? = dynamic> extends self::A {
final field core::double foo;
constructor •({core::double foo = 10.0}) → self::B<self::B::T%>
;
}
static method main() → void
;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
library;
import self as self;
import "dart:core" as core;

abstract class A extends core::Object {
constructor •() → self::A
: super core::Object::•()
;
static factory redir({core::double foo = #C1}) → self::A /* redirection-target: self::B::•<dynamic>*/
return new self::B::•<dynamic>(foo: foo);
}
class B<T extends core::Object? = dynamic> extends self::A {
final field core::double foo;
constructor •({core::double foo = #C1}) → self::B<self::B::T%>
: self::B::foo = foo, super self::A::•()
;
}
static method main() → void {
core::print(new self::B::•<dynamic>().{self::B::foo}{core::double});
}

constants {
#C1 = 10.0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
void main() {}

abstract class A {
A();
factory A.redir({double foo}) = B;
}

class B<T> extends A {
B({this.foo = 10});
final double foo;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
abstract class A {
A();
factory A.redir({double foo}) = B;
}

class B<T> extends A {
B({this.foo = 10});
final double foo;
}

void main() {}

0 comments on commit 39d2653

Please sign in to comment.