mirror of
https://github.com/dart-lang/sdk
synced 2024-09-06 00:59:09 +00:00
[ddc] Fix missing nullability on deferred types
Emits legacy and nullable wrappers to the types that appear in circular hierarchies. There is still missing nullability information if FutureOr appears in the type hierarchy but that fix uncovers a larger issue with the FutureOr type. See https://github.com/dart-lang/sdk/issues/45870. Change-Id: If5894eaff632c5a961f1316d8803032fae2a0ec5 Fixes: https://github.com/dart-lang/sdk/issues/45767 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196600 Commit-Queue: Nicholas Shahan <nshahan@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Reviewed-by: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
cbf16b0731
commit
ec48e8f323
|
@ -94,6 +94,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
/// The class that is emitting its signature information, otherwise null.
|
||||
Class _classEmittingSignatures;
|
||||
|
||||
/// True when a class is emitting a deferred class hierarchy.
|
||||
bool _emittingDeferredType = false;
|
||||
|
||||
/// The current element being loaded.
|
||||
/// We can use this to determine if we're loading top-level code or not:
|
||||
///
|
||||
|
@ -859,22 +862,38 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
return;
|
||||
}
|
||||
|
||||
js_ast.Expression emitDeferredType(DartType t) {
|
||||
assert(isKnownDartTypeImplementor(t));
|
||||
if (t is InterfaceType) {
|
||||
_declareBeforeUse(t.classNode);
|
||||
if (t.typeArguments.isNotEmpty) {
|
||||
return _emitGenericClassType(
|
||||
t, t.typeArguments.map(emitDeferredType));
|
||||
js_ast.Expression emitDeferredType(DartType t,
|
||||
{bool emitNullability = true}) {
|
||||
js_ast.Expression _emitDeferredType(DartType t,
|
||||
{bool emitNullability = true}) {
|
||||
if (t is InterfaceType) {
|
||||
_declareBeforeUse(t.classNode);
|
||||
if (t.typeArguments.isNotEmpty) {
|
||||
var typeRep = _emitGenericClassType(
|
||||
t, t.typeArguments.map(_emitDeferredType));
|
||||
return emitNullability
|
||||
? _emitNullabilityWrapper(typeRep, t.declaredNullability)
|
||||
: typeRep;
|
||||
}
|
||||
return _emitInterfaceType(t, emitNullability: emitNullability);
|
||||
} else if (t is FutureOrType) {
|
||||
_declareBeforeUse(_coreTypes.deprecatedFutureOrClass);
|
||||
// TODO(45870) Add nullability wrappers to FutureOr.
|
||||
return _emitFutureOrTypeWithArgument(
|
||||
_emitDeferredType(t.typeArgument));
|
||||
} else if (t is TypeParameterType) {
|
||||
return _emitTypeParameterType(t, emitNullability: emitNullability);
|
||||
}
|
||||
return _emitInterfaceType(t, emitNullability: false);
|
||||
} else if (t is FutureOrType) {
|
||||
_declareBeforeUse(_coreTypes.deprecatedFutureOrClass);
|
||||
return _emitFutureOrTypeWithArgument(emitDeferredType(t.typeArgument));
|
||||
} else if (t is TypeParameterType) {
|
||||
return _emitTypeParameterType(t, emitNullability: false);
|
||||
return _emitType(t);
|
||||
}
|
||||
return _emitType(t);
|
||||
|
||||
assert(isKnownDartTypeImplementor(t));
|
||||
var savedEmittingDeferredType = _emittingDeferredType;
|
||||
_emittingDeferredType = true;
|
||||
var deferredClassRep =
|
||||
_emitDeferredType(t, emitNullability: emitNullability);
|
||||
_emittingDeferredType = savedEmittingDeferredType;
|
||||
return deferredClassRep;
|
||||
}
|
||||
|
||||
bool shouldDefer(InterfaceType t) {
|
||||
|
@ -921,7 +940,8 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
|
||||
js_ast.Expression getBaseClass(int count) {
|
||||
var base = emitDeferredType(
|
||||
c.getThisType(_coreTypes, c.enclosingLibrary.nonNullable));
|
||||
c.getThisType(_coreTypes, c.enclosingLibrary.nonNullable),
|
||||
emitNullability: false);
|
||||
while (--count >= 0) {
|
||||
base = js.call('#.__proto__', [base]);
|
||||
}
|
||||
|
@ -995,7 +1015,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
var originalSupertype = supertype;
|
||||
deferredSupertypes.add(() => runtimeStatement('setBaseClass(#, #)', [
|
||||
getBaseClass(isMixinAliasClass(c) ? 0 : mixinApplications.length),
|
||||
emitDeferredType(originalSupertype),
|
||||
emitDeferredType(originalSupertype, emitNullability: false),
|
||||
]));
|
||||
// Refers to 'supertype' without type parameters. We remove these from
|
||||
// the 'extends' clause for generics for cyclic dependencies and append
|
||||
|
@ -1015,7 +1035,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
|
||||
var m = c.mixedInType.asInterfaceType;
|
||||
var deferMixin = shouldDefer(m);
|
||||
var mixinClass = deferMixin ? emitDeferredType(m) : emitClassRef(m);
|
||||
var mixinClass = deferMixin
|
||||
? emitDeferredType(m, emitNullability: false)
|
||||
: emitClassRef(m);
|
||||
var classExpr = deferMixin ? getBaseClass(0) : className;
|
||||
|
||||
var mixinApplication =
|
||||
|
@ -1087,7 +1109,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
if (shouldDefer(mixinType)) {
|
||||
deferredSupertypes.add(() => runtimeStatement('applyMixin(#, #)', [
|
||||
getBaseClass(mixinApplications.length - i),
|
||||
emitDeferredType(mixinType)
|
||||
emitDeferredType(mixinType, emitNullability: false)
|
||||
]));
|
||||
} else {
|
||||
body.add(runtimeStatement(
|
||||
|
@ -2909,7 +2931,9 @@ class ProgramCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
|
|||
_currentClass != null && identical(_currentClass, _classEmittingExtends);
|
||||
|
||||
bool get _cacheTypes =>
|
||||
!_emittingClassExtends && !_emittingClassSignatures ||
|
||||
!_emittingDeferredType &&
|
||||
!_emittingClassExtends &&
|
||||
!_emittingClassSignatures ||
|
||||
_currentFunction != null;
|
||||
|
||||
js_ast.Expression _emitGenericClassType(
|
||||
|
|
25
tests/language/generic/regress_45767_test.dart
Normal file
25
tests/language/generic/regress_45767_test.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
// Regression test for https://github.com/dart-lang/sdk/issues/45767.
|
||||
|
||||
class I<T> {}
|
||||
|
||||
class F<E extends F<E>> extends I<E> {}
|
||||
|
||||
// Extending F forces DDC to defer the superclass and trigger the bug
|
||||
class A<T> extends F<A<Object?>> {}
|
||||
|
||||
class B<T> extends F<B<T?>> {}
|
||||
|
||||
void main() {
|
||||
Expect.isTrue(A<bool>() is I<A<Object?>>);
|
||||
Expect.equals(!hasSoundNullSafety, A<bool>() is I<A<Object>>);
|
||||
Expect.isTrue(B<bool>() is I<B<bool?>>);
|
||||
Expect.equals(!hasSoundNullSafety, B<bool>() is I<B<bool>>);
|
||||
}
|
Loading…
Reference in a new issue