[cfe] Handle type dependencies through SynthesizedSourceConstructorBuilder

The types of forwarding constructor parameters are required for
super parameters. In order to handle the dependency correct and
computed the right substitutions through the inheritance chain,
SynthesizedSourceConstructorBuilder now use inferFormalTypes to trigger
copying of inferred parameter types.

Closes https://github.com/dart-lang/sdk/issues/48708

Change-Id: I365ea5ea1fe5de0ac2422d01878b7f1eac421d24
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239427
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Johnni Winther 2022-03-31 05:26:01 +00:00 committed by Commit Bot
parent 3feede1457
commit 10bbfbebc9
15 changed files with 333 additions and 33 deletions

View file

@ -250,11 +250,13 @@ class TypeDependency {
final Member original;
final Substitution substitution;
final bool copyReturnType;
bool _hasBeenInferred = false;
TypeDependency(this.synthesized, this.original, this.substitution,
{required this.copyReturnType});
void copyInferred() {
if (_hasBeenInferred) return;
for (int i = 0; i < original.function!.positionalParameters.length; i++) {
VariableDeclaration synthesizedParameter =
synthesized.function!.positionalParameters[i];
@ -275,5 +277,6 @@ class TypeDependency {
synthesized.function!.returnType =
substitution.substituteType(original.function!.returnType);
}
_hasBeenInferred = true;
}
}

View file

@ -1088,11 +1088,11 @@ class KernelTarget extends TargetImplementation {
//..fileEndOffset = cls.fileOffset
..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault;
TypeDependency? typeDependency;
if (hasTypeDependency) {
loader.registerTypeDependency(
constructor,
new TypeDependency(constructor, superConstructor, substitution,
copyReturnType: false));
typeDependency = new TypeDependency(
constructor, superConstructor, substitution,
copyReturnType: false);
}
Procedure? constructorTearOff = createConstructorTearOffProcedure(
@ -1107,16 +1107,20 @@ class KernelTarget extends TargetImplementation {
buildConstructorTearOffProcedure(constructorTearOff, constructor,
classBuilder.cls, classBuilder.libraryBuilder);
}
return new SyntheticSourceConstructorBuilder(
classBuilder, constructor, constructorTearOff,
// We pass on the original constructor and the cloned function nodes to
// ensure that the default values are computed and cloned for the
// outline. It is needed to make the default values a part of the
// outline for const constructors, and additionally it is required for
// a potential subclass using super initializing parameters that will
// required the cloning of the default values.
definingConstructor: superConstructorBuilder,
delayedDefaultValueCloner: delayedDefaultValueCloner);
SyntheticSourceConstructorBuilder constructorBuilder =
new SyntheticSourceConstructorBuilder(
classBuilder, constructor, constructorTearOff,
// We pass on the original constructor and the cloned function nodes
// to ensure that the default values are computed and cloned for the
// outline. It is needed to make the default values a part of the
// outline for const constructors, and additionally it is required
// for a potential subclass using super initializing parameters that
// will required the cloning of the default values.
definingConstructor: superConstructorBuilder,
delayedDefaultValueCloner: delayedDefaultValueCloner,
typeDependency: typeDependency);
loader.registerConstructorToBeInferred(constructor, constructorBuilder);
return constructorBuilder;
}
void finishSynthesizedParameters({bool forOutline = false}) {

View file

@ -26,7 +26,8 @@ import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/expression_generator_helper.dart';
import '../kernel/hierarchy/class_member.dart' show ClassMember;
import '../kernel/kernel_helper.dart' show DelayedDefaultValueCloner;
import '../kernel/kernel_helper.dart'
show DelayedDefaultValueCloner, TypeDependency;
import '../kernel/utils.dart'
show isRedirectingGenerativeConstructorImplementation;
import '../messages.dart'
@ -52,6 +53,9 @@ import 'source_function_builder.dart';
abstract class SourceConstructorBuilder
implements ConstructorBuilder, SourceMemberBuilder {
/// Infers the types of any untyped initializing formals.
void inferFormalTypes(TypeEnvironment typeEnvironment);
void addSuperParameterDefaultValueCloners(
List<DelayedDefaultValueCloner> delayedDefaultValueCloners);
}
@ -224,7 +228,7 @@ class DeclaredSourceConstructorBuilder extends SourceFunctionBuilderImpl
return _constructor;
}
/// Infers the types of any untyped initializing formals.
@override
void inferFormalTypes(TypeEnvironment typeEnvironment) {
if (_hasFormalsInferred) return;
if (formals != null) {
@ -321,14 +325,8 @@ class DeclaredSourceConstructorBuilder extends SourceFunctionBuilderImpl
ConstructorBuilder? superTargetBuilder =
_computeSuperTargetBuilder(initializers);
if (superTargetBuilder is DeclaredSourceConstructorBuilder) {
if (superTargetBuilder is SourceConstructorBuilder) {
superTargetBuilder.inferFormalTypes(typeEnvironment);
} else if (superTargetBuilder is SyntheticSourceConstructorBuilder) {
MemberBuilder? superTargetOriginBuilder =
superTargetBuilder._effectivelyDefiningConstructor;
if (superTargetOriginBuilder is DeclaredSourceConstructorBuilder) {
superTargetOriginBuilder.inferFormalTypes(typeEnvironment);
}
}
Constructor superTarget;
@ -339,10 +337,9 @@ class DeclaredSourceConstructorBuilder extends SourceFunctionBuilderImpl
superFormals = superTargetBuilder.formals!;
} else if (superTargetBuilder is DillConstructorBuilder) {
superTarget = superTargetBuilder.constructor;
superConstructorFunction = superTargetBuilder.function;
if (superTargetBuilder is SyntheticSourceConstructorBuilder) {
superFormals = superTargetBuilder.formals;
} else {
superConstructorFunction = superTargetBuilder.function;
}
} else {
// The error in this case should be reported elsewhere. Here we perform a
@ -354,7 +351,27 @@ class DeclaredSourceConstructorBuilder extends SourceFunctionBuilderImpl
List<bool> positionalSuperFormalHasInitializer = [];
Map<String, DartType?> namedSuperFormalType = {};
Map<String, bool> namedSuperFormalHasInitializer = {};
if (superFormals != null) {
// TODO(johnniwinther): Clean this up when [VariableDeclaration] has a
// `hasDeclaredInitializer` flag.
if (superFormals != null && superConstructorFunction != null) {
for (VariableDeclaration formal
in superConstructorFunction.positionalParameters) {
positionalSuperFormalType.add(formal.type);
}
for (VariableDeclaration formal
in superConstructorFunction.namedParameters) {
namedSuperFormalType[formal.name!] = formal.type;
}
for (FormalParameterBuilder formal in superFormals) {
if (formal.isPositional) {
positionalSuperFormalHasInitializer
.add(formal.hasDeclaredInitializer);
} else {
namedSuperFormalHasInitializer[formal.name] =
formal.hasDeclaredInitializer;
}
}
} else if (superFormals != null) {
for (FormalParameterBuilder formal in superFormals) {
if (formal.isPositional) {
positionalSuperFormalType.add(formal.variable?.type);
@ -802,19 +819,34 @@ class SyntheticSourceConstructorBuilder extends DillConstructorBuilder
/// the constructor that effectively defines this constructor.
MemberBuilder? _immediatelyDefiningConstructor;
DelayedDefaultValueCloner? _delayedDefaultValueCloner;
TypeDependency? _typeDependency;
SyntheticSourceConstructorBuilder(SourceClassBuilder parent,
Constructor constructor, Procedure? constructorTearOff,
{MemberBuilder? definingConstructor,
DelayedDefaultValueCloner? delayedDefaultValueCloner})
DelayedDefaultValueCloner? delayedDefaultValueCloner,
TypeDependency? typeDependency})
: _immediatelyDefiningConstructor = definingConstructor,
_delayedDefaultValueCloner = delayedDefaultValueCloner,
_typeDependency = typeDependency,
super(constructor, constructorTearOff, parent);
@override
SourceLibraryBuilder get libraryBuilder =>
super.libraryBuilder as SourceLibraryBuilder;
@override
void inferFormalTypes(TypeEnvironment typeEnvironment) {
if (_immediatelyDefiningConstructor is SourceConstructorBuilder) {
(_immediatelyDefiningConstructor as SourceConstructorBuilder)
.inferFormalTypes(typeEnvironment);
}
if (_typeDependency != null) {
_typeDependency!.copyInferred();
_typeDependency = null;
}
}
MemberBuilder? get _effectivelyDefiningConstructor {
MemberBuilder? origin = _immediatelyDefiningConstructor;
while (origin is SyntheticSourceConstructorBuilder) {

View file

@ -974,7 +974,7 @@ severity: $severity
}
void registerConstructorToBeInferred(
Constructor constructor, DeclaredSourceConstructorBuilder builder) {
Constructor constructor, SourceConstructorBuilder builder) {
_typeInferenceEngine!.toBeInferred[constructor] = builder;
}

View file

@ -113,14 +113,14 @@ abstract class TypeInferenceEngine {
/// This is represented as a map from a constructor to its library
/// builder because the builder is used to report errors due to cyclic
/// inference dependencies.
final Map<Constructor, DeclaredSourceConstructorBuilder> toBeInferred = {};
final Map<Constructor, SourceConstructorBuilder> toBeInferred = {};
/// A map containing constructors in the process of being inferred.
///
/// This is used to detect cyclic inference dependencies. It is represented
/// as a map from a constructor to its library builder because the builder
/// is used to report errors.
final Map<Constructor, DeclaredSourceConstructorBuilder> beingInferred = {};
final Map<Constructor, SourceConstructorBuilder> beingInferred = {};
final Map<Member, TypeDependency> typeDependencies = {};
@ -144,7 +144,7 @@ abstract class TypeInferenceEngine {
void finishTopLevelInitializingFormals() {
// Field types have all been inferred so we don't need to guard against
// cyclic dependency.
for (DeclaredSourceConstructorBuilder builder in toBeInferred.values) {
for (SourceConstructorBuilder builder in toBeInferred.values) {
builder.inferFormalTypes(typeSchemaEnvironment);
}
toBeInferred.clear();

View file

@ -388,8 +388,7 @@ class TypeInferrerImpl implements TypeInferrer {
@override
void inferConstructorParameterTypes(Constructor target) {
DeclaredSourceConstructorBuilder? constructor =
engine.beingInferred[target];
SourceConstructorBuilder? constructor = engine.beingInferred[target];
if (constructor != null) {
// There is a cyclic dependency where inferring the types of the
// initializing formals of a constructor required us to infer the

View file

@ -0,0 +1,23 @@
// Copyright (c) 2022, 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.
class A {}
class Mixin {}
abstract class B<D> {
B({
required this.field,
});
final D field;
}
class C extends B<A> with Mixin {
C({
required super.field,
});
}
main() {}

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
: super core::Object::•()
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
: self::B::field = field, super core::Object::•()
;
}
abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/ {
synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field = #C1}) → self::C
: super self::_C&B&Mixin::•(field: field)
;
}
static method main() → dynamic {}
constants {
#C1 = null
}

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
: super core::Object::•()
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
: self::B::field = field, super core::Object::•()
;
}
abstract class _C&B&Mixin extends self::B<self::A> implements self::Mixin /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field = #C1}) → self::C
: super self::_C&B&Mixin::•(field: field)
;
}
static method main() → dynamic {}
constants {
#C1 = null
}

View file

@ -0,0 +1,18 @@
class A {}
class Mixin {}
abstract class B<D> {
B({
required this.field,
});
final D field;
}
class C extends B<A> with Mixin {
C({
required super.field,
});
}
main() {}

View file

@ -0,0 +1,18 @@
abstract class B<D> {
B({
required this.field,
});
final D field;
}
class A {}
class C extends B<A> with Mixin {
C({
required super.field,
});
}
class Mixin {}
main() {}

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
: super core::Object::•()
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
: self::B::field = field, super core::Object::•()
;
}
abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/ {
synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field = #C1}) → self::C
: super self::_C&B&Mixin::•(field: field)
;
}
static method main() → dynamic {}
constants {
#C1 = null
}

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
: super core::Object::•()
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
: self::B::field = field, super core::Object::•()
;
}
abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/ {
synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field = #C1}) → self::C
: super self::_C&B&Mixin::•(field: field)
;
}
static method main() → dynamic {}
constants {
#C1 = null
}

View file

@ -0,0 +1,28 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field}) → self::B<self::B::D%>
;
}
abstract class _C&B&Mixin = self::B<self::A> with self::Mixin /*isAnonymousMixin*/ {
synthetic constructor •({self::A field}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field}) → self::C
;
}
static method main() → dynamic
;

View file

@ -0,0 +1,35 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class Mixin extends core::Object {
synthetic constructor •() → self::Mixin
: super core::Object::•()
;
}
abstract class B<D extends core::Object? = dynamic> extends core::Object {
final field self::B::D% field;
constructor •({required self::B::D% field = #C1}) → self::B<self::B::D%>
: self::B::field = field, super core::Object::•()
;
}
abstract class _C&B&Mixin extends self::B<self::A> implements self::Mixin /*isAnonymousMixin,isEliminatedMixin*/ {
synthetic constructor •({self::A field = #C1}) → self::_C&B&Mixin
: super self::B::•(field: field)
;
}
class C extends self::_C&B&Mixin {
constructor •({required self::A field = #C1}) → self::C
: super self::_C&B&Mixin::•(field: field)
;
}
static method main() → dynamic {}
constants {
#C1 = null
}