mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:44:59 +00:00
[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:
parent
3feede1457
commit
10bbfbebc9
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -974,7 +974,7 @@ severity: $severity
|
|||
}
|
||||
|
||||
void registerConstructorToBeInferred(
|
||||
Constructor constructor, DeclaredSourceConstructorBuilder builder) {
|
||||
Constructor constructor, SourceConstructorBuilder builder) {
|
||||
_typeInferenceEngine!.toBeInferred[constructor] = builder;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
23
pkg/front_end/testcases/super_parameters/issue48708.dart
Normal file
23
pkg/front_end/testcases/super_parameters/issue48708.dart
Normal 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() {}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
;
|
|
@ -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
|
||||
}
|
Loading…
Reference in a new issue