Skip to content

Commit 2bc9025

Browse files
Kevin Millikincommit-bot@chromium.org
Kevin Millikin
authored andcommitted
Consider initializing formals during top-level type inference
The previous implementation assumed that initializing formals without type annotations could be inferred after the rest of top-level type inference was completely done. This is not necessarily the case because inferred types for initializing formals depend on inferred types for fields which in turn depend on inferred types for constructors used in their initializers. Fixes #32866 Change-Id: I9de0b865c740d7542e5f5ad3d62c4c47c4532266 Reviewed-on: https://dart-review.googlesource.com/60140 Commit-Queue: Kevin Millikin <[email protected]> Reviewed-by: Dmitry Stefantsov <[email protected]>
1 parent 9df6426 commit 2bc9025

11 files changed

+205
-66
lines changed

pkg/front_end/lib/src/fasta/kernel/kernel_formal_parameter_builder.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ class KernelFormalParameterBuilder
4646
isFieldFormal: hasThis,
4747
isCovariant: isCovariant)
4848
..fileOffset = charOffset;
49-
if (type == null && hasThis) {
50-
library.loader.typeInferenceEngine
51-
.recordInitializingFormal(declaration);
52-
}
5349
}
5450
return declaration;
5551
}

pkg/front_end/lib/src/fasta/kernel/kernel_procedure_builder.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,15 @@ class KernelConstructorBuilder extends KernelFunctionBuilder {
444444
return isRedirectingGenerativeConstructorImplementation(constructor);
445445
}
446446

447+
bool get isEligibleForTopLevelInference {
448+
if (formals != null) {
449+
for (var formal in formals) {
450+
if (formal.type == null && formal.hasThis) return true;
451+
}
452+
}
453+
return false;
454+
}
455+
447456
Constructor build(SourceLibraryBuilder library) {
448457
if (constructor.name == null) {
449458
constructor.function = buildFunction(library);
@@ -455,6 +464,14 @@ class KernelConstructorBuilder extends KernelFunctionBuilder {
455464
constructor.isExternal = isExternal;
456465
constructor.name = new Name(name, library.target);
457466
}
467+
if (!library.disableTypeInference && isEligibleForTopLevelInference) {
468+
for (KernelFormalParameterBuilder formal in formals) {
469+
if (formal.type == null && formal.hasThis) {
470+
formal.declaration.type = null;
471+
}
472+
}
473+
library.loader.typeInferenceEngine.toBeInferred[constructor] = library;
474+
}
458475
return constructor;
459476
}
460477

pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ import '../../base/instrumentation.dart'
3434
InstrumentationValueForTypeArgs;
3535

3636
import '../fasta_codes.dart'
37-
show noLength, templateCantUseSuperBoundedTypeForInstanceCreation;
37+
show
38+
noLength,
39+
templateCantInferTypeDueToCircularity,
40+
templateCantUseSuperBoundedTypeForInstanceCreation;
3841

3942
import '../problems.dart' show unhandled, unsupported;
4043

@@ -607,6 +610,36 @@ class ShadowConstructorInvocation extends ConstructorInvocation
607610

608611
@override
609612
DartType _inferExpression(ShadowTypeInferrer inferrer, DartType typeContext) {
613+
var library = inferrer.engine.beingInferred[target];
614+
if (library != null) {
615+
// There is a cyclic dependency where inferring the types of the
616+
// initializing formals of a constructor required us to infer the
617+
// corresponding field type which required us to know the type of the
618+
// constructor.
619+
var name = target.enclosingClass.name;
620+
if (target.name.name != '') name += '.${target.name.name}';
621+
library.addProblem(
622+
templateCantInferTypeDueToCircularity.withArguments(name),
623+
target.fileOffset,
624+
name.length,
625+
target.fileUri);
626+
for (var declaration in target.function.positionalParameters) {
627+
declaration.type ??= const DynamicType();
628+
}
629+
for (var declaration in target.function.namedParameters) {
630+
declaration.type ??= const DynamicType();
631+
}
632+
} else if ((library = inferrer.engine.toBeInferred[target]) != null) {
633+
inferrer.engine.toBeInferred.remove(target);
634+
inferrer.engine.beingInferred[target] = library;
635+
for (var declaration in target.function.positionalParameters) {
636+
inferrer.engine.inferInitializingFormal(declaration, target);
637+
}
638+
for (var declaration in target.function.namedParameters) {
639+
inferrer.engine.inferInitializingFormal(declaration, target);
640+
}
641+
inferrer.engine.beingInferred.remove(target);
642+
}
610643
var inferredType = inferrer.inferInvocation(
611644
typeContext,
612645
fileOffset,
@@ -807,7 +840,7 @@ class ShadowFactoryConstructorInvocation extends StaticInvocation
807840
/// Concrete shadow object representing a field in kernel form.
808841
class ShadowField extends Field implements ShadowMember {
809842
@override
810-
InferenceNode _inferenceNode;
843+
InferenceNode inferenceNode;
811844

812845
ShadowTypeInferrer _typeInferrer;
813846

@@ -823,13 +856,13 @@ class ShadowField extends Field implements ShadowMember {
823856
}
824857

825858
static bool hasTypeInferredFromInitializer(ShadowField field) =>
826-
field._inferenceNode is FieldInitializerInferenceNode;
859+
field.inferenceNode is FieldInitializerInferenceNode;
827860

828861
static bool isImplicitlyTyped(ShadowField field) => field._isImplicitlyTyped;
829862

830863
static void setInferenceNode(ShadowField field, InferenceNode node) {
831-
assert(field._inferenceNode == null);
832-
field._inferenceNode = node;
864+
assert(field.inferenceNode == null);
865+
field.inferenceNode = node;
833866
}
834867
}
835868

@@ -1428,18 +1461,18 @@ class ShadowMapLiteral extends MapLiteral implements ShadowExpression {
14281461
abstract class ShadowMember implements Member {
14291462
Uri get fileUri;
14301463

1431-
InferenceNode get _inferenceNode;
1464+
InferenceNode get inferenceNode;
14321465

1433-
void set _inferenceNode(InferenceNode value);
1466+
void set inferenceNode(InferenceNode value);
14341467

14351468
void setInferredType(
14361469
TypeInferenceEngine engine, Uri uri, DartType inferredType);
14371470

14381471
static void resolveInferenceNode(Member member) {
14391472
if (member is ShadowMember) {
1440-
if (member._inferenceNode != null) {
1441-
member._inferenceNode.resolve();
1442-
member._inferenceNode = null;
1473+
if (member.inferenceNode != null) {
1474+
member.inferenceNode.resolve();
1475+
member.inferenceNode = null;
14431476
}
14441477
}
14451478
}
@@ -1568,7 +1601,7 @@ class ShadowNullLiteral extends NullLiteral implements ShadowExpression {
15681601
/// Concrete shadow object representing a procedure in kernel form.
15691602
class ShadowProcedure extends Procedure implements ShadowMember {
15701603
@override
1571-
InferenceNode _inferenceNode;
1604+
InferenceNode inferenceNode;
15721605

15731606
final bool _hasImplicitReturnType;
15741607

@@ -1757,9 +1790,9 @@ class ShadowStaticAssignment extends ShadowComplexAssignment {
17571790
if (write is StaticSet) {
17581791
writeContext = write.target.setterType;
17591792
writeMember = write.target;
1760-
if (writeMember is ShadowField && writeMember._inferenceNode != null) {
1761-
writeMember._inferenceNode.resolve();
1762-
writeMember._inferenceNode = null;
1793+
if (writeMember is ShadowField && writeMember.inferenceNode != null) {
1794+
writeMember.inferenceNode.resolve();
1795+
writeMember.inferenceNode = null;
17631796
}
17641797
}
17651798
var inferredResult = _inferRhs(inferrer, readType, writeContext);
@@ -1776,9 +1809,9 @@ class ShadowStaticGet extends StaticGet implements ShadowExpression {
17761809
@override
17771810
DartType _inferExpression(ShadowTypeInferrer inferrer, DartType typeContext) {
17781811
var target = this.target;
1779-
if (target is ShadowField && target._inferenceNode != null) {
1780-
target._inferenceNode.resolve();
1781-
target._inferenceNode = null;
1812+
if (target is ShadowField && target.inferenceNode != null) {
1813+
target.inferenceNode.resolve();
1814+
target.inferenceNode = null;
17821815
}
17831816
var type = target.getterType;
17841817
if (target is Procedure && target.kind == ProcedureKind.Method) {

pkg/front_end/lib/src/fasta/kernel/kernel_target.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -514,10 +514,12 @@ class KernelTarget extends TargetImplementation {
514514
Constructor constructor, Map<TypeParameter, DartType> substitutionMap) {
515515
VariableDeclaration copyFormal(VariableDeclaration formal) {
516516
// TODO(ahe): Handle initializers.
517-
return new VariableDeclaration(formal.name,
518-
type: substitute(formal.type, substitutionMap),
519-
isFinal: formal.isFinal,
520-
isConst: formal.isConst);
517+
var copy = new VariableDeclaration(formal.name,
518+
isFinal: formal.isFinal, isConst: formal.isConst);
519+
if (formal.type != null) {
520+
copy.type = substitute(formal.type, substitutionMap);
521+
}
522+
return copy;
521523
}
522524

523525
List<VariableDeclaration> positionalParameters = <VariableDeclaration>[];

pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,26 @@
44

55
import 'package:kernel/ast.dart'
66
show
7-
Class,
7+
Constructor,
88
DartType,
99
DartTypeVisitor,
1010
DynamicType,
1111
FunctionType,
1212
InterfaceType,
13-
Location,
1413
TypeParameter,
1514
TypeParameterType,
16-
TypedefType;
15+
TypedefType,
16+
VariableDeclaration;
1717
import 'package:kernel/class_hierarchy.dart';
1818
import 'package:kernel/core_types.dart';
1919

2020
import '../../base/instrumentation.dart';
2121

2222
import '../builder/library_builder.dart';
2323

24-
import '../deprecated_problems.dart' show Crash;
25-
2624
import '../kernel/kernel_shadow_ast.dart';
2725

28-
import '../messages.dart'
29-
show getLocationFromNode, noLength, templateCantInferTypeDueToCircularity;
26+
import '../messages.dart' show noLength, templateCantInferTypeDueToCircularity;
3027

3128
import '../source/source_library_builder.dart';
3229

@@ -211,7 +208,20 @@ abstract class TypeInferenceEngine {
211208

212209
final staticInferenceNodes = <FieldInitializerInferenceNode>[];
213210

214-
final initializingFormals = <ShadowVariableDeclaration>[];
211+
/// A map containing constructors with initializing formals whose types
212+
/// need to be inferred.
213+
///
214+
/// This is represented as a map from a constructor to its library
215+
/// builder because the builder is used to report errors due to cyclic
216+
/// inference dependencies.
217+
final Map<Constructor, LibraryBuilder> toBeInferred = {};
218+
219+
/// A map containing constructors in the process of being inferred.
220+
///
221+
/// This is used to detect cyclic inference dependencies. It is represented
222+
/// as a map from a constructor to its library builder because the builder
223+
/// is used to report errors.
224+
final Map<Constructor, LibraryBuilder> beingInferred = {};
215225

216226
final Instrumentation instrumentation;
217227

@@ -248,20 +258,37 @@ abstract class TypeInferenceEngine {
248258
}
249259

250260
/// Performs the third phase of top level inference, which is to visit all
251-
/// initializing formals and infer their types (if necessary) from the
252-
/// corresponding fields.
261+
/// constructors still needing inference and infer the types of their
262+
/// initializing formals from the corresponding fields.
253263
void finishTopLevelInitializingFormals() {
254-
for (ShadowVariableDeclaration formal in initializingFormals) {
255-
try {
256-
formal.type = _inferInitializingFormalType(formal);
257-
} catch (e, s) {
258-
Location location = getLocationFromNode(formal);
259-
if (location == null) {
260-
rethrow;
261-
} else {
262-
throw new Crash(location.file, formal.fileOffset, e, s);
264+
// Field types have all been inferred so there cannot be a cyclic
265+
// dependency.
266+
for (Constructor constructor in toBeInferred.keys) {
267+
for (var declaration in constructor.function.positionalParameters) {
268+
inferInitializingFormal(declaration, constructor);
269+
}
270+
for (var declaration in constructor.function.namedParameters) {
271+
inferInitializingFormal(declaration, constructor);
272+
}
273+
}
274+
toBeInferred.clear();
275+
}
276+
277+
void inferInitializingFormal(VariableDeclaration formal, Constructor parent) {
278+
if (formal.type == null) {
279+
for (ShadowField field in parent.enclosingClass.fields) {
280+
if (field.name.name == formal.name) {
281+
if (field.inferenceNode != null) {
282+
field.inferenceNode.resolve();
283+
}
284+
formal.type = field.type;
285+
return;
263286
}
264287
}
288+
// We did not find the corresponding field, so the program is erroneous.
289+
// The error should have been reported elsewhere and type inference
290+
// should continue by inferring dynamic.
291+
formal.type = const DynamicType();
265292
}
266293
}
267294

@@ -274,33 +301,11 @@ abstract class TypeInferenceEngine {
274301
new TypeSchemaEnvironment(coreTypes, hierarchy, strongMode);
275302
}
276303

277-
/// Records that the given initializing [formal] will need top level type
278-
/// inference.
279-
void recordInitializingFormal(ShadowVariableDeclaration formal) {
280-
initializingFormals.add(formal);
281-
}
282-
283304
/// Records that the given static [field] will need top level type inference.
284305
void recordStaticFieldInferenceCandidate(
285306
ShadowField field, LibraryBuilder library) {
286307
var node = new FieldInitializerInferenceNode(this, field, library);
287308
ShadowField.setInferenceNode(field, node);
288309
staticInferenceNodes.add(node);
289310
}
290-
291-
DartType _inferInitializingFormalType(ShadowVariableDeclaration formal) {
292-
assert(ShadowVariableDeclaration.isImplicitlyTyped(formal));
293-
var enclosingClass = formal.parent?.parent?.parent;
294-
if (enclosingClass is Class) {
295-
for (var field in enclosingClass.fields) {
296-
if (field.name.name == formal.name) {
297-
return field.type;
298-
}
299-
}
300-
}
301-
// No matching field, or something else has gone wrong (e.g. initializing
302-
// formal outside of a class declaration). The error should be reported
303-
// elsewhere, so just infer `dynamic`.
304-
return const DynamicType();
305-
}
306311
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) 2018, 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+
// This test is intended to trigger a circular top-level type-inference
6+
// dependency involving an initializing formal where the circularity is detected
7+
// when inferring the type of the constructor.
8+
//
9+
// The compiler should generate an error message and it should be properly
10+
// formatted including offset and lenght of the constructor.
11+
var x = new C._circular(null);
12+
13+
class C {
14+
var f = new C._circular(null);
15+
C._circular(this.f);
16+
}
17+
18+
main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object {
6+
field dynamic f = new self::C::_circular(null);
7+
constructor _circular(dynamic f) → void
8+
: self::C::f = f, super core::Object::•()
9+
;
10+
}
11+
static field dynamic x = new self::C::_circular(null);
12+
static method main() → dynamic {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object {
6+
field dynamic f = new self::C::_circular(null);
7+
constructor _circular(dynamic f) → void
8+
: self::C::f = f, super core::Object::•()
9+
;
10+
}
11+
static field dynamic x = new self::C::_circular(null);
12+
static method main() → dynamic {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C extends core::Object {
6+
field dynamic f;
7+
constructor _circular(dynamic f) → void
8+
;
9+
}
10+
static field dynamic x;
11+
static method main() → dynamic
12+
;

0 commit comments

Comments
 (0)