Files
sdk/pkg/kernel/lib/src/non_null.dart
T
Chloe Stefantsova 3393befc99 [cfe] Remove constructors for alpha renaming
In this CL methods StructuralParameterType.forAlphaRenaming,
StructuralParameterType.forAlphaRenamingFromTypeParameter,
TypeParameterType.forAlphaRenaming, and
TypeParameterType.forAlphaRenamingFromStructuralParameter are removed,
and their call sites are replaced with invocations of other
constructors of StructuralParameterType and TypeParameterType. The
reason for this change is call sites having more information for
correct computing of type nullabilities.

In addition to the primary update, the following related changes are
made in this CL.

* Method StructuralParameterType.computeDefaultNullabilityForLibrary
  is renamed into StructuralParameterType.computeDefaultNullability,
  and TypeParameterType.computeDefaultNullabilityForLibrary is renamed
  into TypeParameterType.computeDefaultNullability. The parameter
  `library` is removed from both methods, since it's no longer needed.
* The static methods named `computeNullabilityFromBound` are removed
  from `StructuralParameterType` and `TypeParameterType` and
  re-introduced as instance members in classes `StructuralParameter`
  and `TestParameter` respectively

TEST=existing

Change-Id: I26cccf17ccc9bda1e8b750196f427325b544a7ac
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402820
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2025-01-07 01:16:48 -08:00

243 lines
7.6 KiB
Dart

// Copyright (c) 2019, 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 '../ast.dart';
import '../type_algebra.dart';
/// Returns the type defined as `NonNull(type)` in the nnbd specification.
DartType computeNonNull(DartType type) {
if (type.nullability == Nullability.nonNullable) {
// The visitor below always returns null when the input type is already
// nullable, subsequently returning the input type.
// When compiling "compile.dart" this is exactly what happens ~42% of the
// time. Here we short-circuit the visit.
// Note that some use [declaredNullability] instead of [nullability], but
// that [nullability] is only nonNullable if [declaredNullability] is too.
assert(type.accept(const _NonNullVisitor()) == null);
return type;
}
return type.accept(const _NonNullVisitor()) ?? type;
}
/// Visitor that computes the `NonNull` function defined in the nnbd
/// specification.
///
/// The visitor returns `null` if `NonNull(T) = T`.
class _NonNullVisitor implements DartTypeVisitor<DartType?> {
const _NonNullVisitor();
@override
DartType? visitAuxiliaryType(AuxiliaryType node) {
throw new UnsupportedError(
"Unsupported auxiliary type ${node} (${node.runtimeType}).");
}
@override
DartType? visitDynamicType(DynamicType node) {
// NonNull(dynamic) = dynamic
return null;
}
@override
DartType? visitFunctionType(FunctionType node) {
// NonNull(T0 Function(...)) = T0 Function(...)
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return node.withDeclaredNullability(Nullability.nonNullable);
}
@override
DartType? visitRecordType(RecordType node) {
// By analogy with FunctionType.
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return node.withDeclaredNullability(Nullability.nonNullable);
}
@override
DartType? visitFutureOrType(FutureOrType node) {
// NonNull(FutureOr<T>) = FutureOr<T>
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
// Note that we should _not_ compute NonNull of the type argument. Consider
//
// NonNull(FutureOr<int?>?)
//
// We have that
//
// FutureOr<int?>? = Future<int?>? | int?
//
// and therefore that
//
// NonNull(FutureOr<int?>?) = NonNull(FutureOr<int?>?) | NonNull(int?)
// = FutureOr<int?> | int
//
// but that means that while `null` is not a possible value from `int` it
// is still a possible value from awaiting the future. Taking NonNull on
// the type argument as well as on the `FutureOr`:
//
// NonNull(FutureOr<int?>?) = NonNull(FutureOr<NonNull(int?)>?)
// = FutureOr<int>
//
// would be wrong since it would compute that the awaited result could not
// be `null`.
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return new FutureOrType(node.typeArgument, Nullability.nonNullable);
}
@override
DartType? visitInterfaceType(InterfaceType node) {
// NonNull(C<T1, ... , Tn>) = C<T1, ... , Tn> for class C other
// than Null (including Object).
//
// NonNull(Function) = Function
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return node.withDeclaredNullability(Nullability.nonNullable);
}
@override
DartType? visitExtensionType(ExtensionType node) {
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return node.withDeclaredNullability(Nullability.nonNullable);
}
@override
DartType? visitInvalidType(InvalidType node) => null;
@override
DartType? visitNeverType(NeverType node) {
// NonNull(Never) = Never
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return const NeverType.nonNullable();
}
@override
DartType? visitNullType(NullType node) {
// NonNull(Null) = Never
return const NeverType.nonNullable();
}
@override
DartType? visitTypeParameterType(TypeParameterType node) {
// NonNull(X) = X & NonNull(B), where B is the bound of X.
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.nullability == Nullability.nonNullable) {
return null;
}
// NonNull(X) = X & NonNull(B), where B is the bound of X.
if (node.bound.nullability == Nullability.nonNullable) {
// The bound is already non-nullable so we set the declared nullability
// to non-nullable.
return node.withDeclaredNullability(Nullability.nonNullable);
}
DartType? bound = node.bound.accept(this);
if (bound == null) {
// The bound could not be made non-nullable so we set the declared
// nullability to undetermined.
if (node.declaredNullability == Nullability.undetermined) {
return null;
}
return node.withDeclaredNullability(Nullability.undetermined);
} else {
// The nullability is fully determined by the bound so we pass the
// default nullability for the declared nullability.
return new IntersectionType(
new TypeParameterType.withDefaultNullability(node.parameter), bound);
}
}
@override
DartType? visitStructuralParameterType(StructuralParameterType node) {
return null;
}
@override
DartType? visitIntersectionType(IntersectionType node) {
// NonNull(X & T) = X & NonNull(T)
if (node.nullability == Nullability.nonNullable) {
return null;
}
if (node.right.nullability == Nullability.nonNullable) {
// The RHS is already non-nullable so nothing should be changed.
return node.withDeclaredNullability(Nullability.nonNullable);
}
DartType? right = node.right.accept(this);
if (right == null) {
// The RHS could not be made non-nullable so we set the
// declared nullability to undetermined.
if (node.left.declaredNullability == Nullability.undetermined) {
return null;
}
return new IntersectionType(
new TypeParameterType(node.left.parameter, Nullability.undetermined),
node.right);
} else if (right.nullability == Nullability.nonNullable) {
// The bound could be made non-nullable so we use it as the promoted
// bound.
return new IntersectionType(
computeTypeWithoutNullabilityMarker(node.left) as TypeParameterType,
right);
} else {
// The bound could not be made non-nullable so we use it as the promoted
// bound with undetermined nullability.
return new IntersectionType(
new TypeParameterType(node.left.parameter, Nullability.undetermined),
right);
}
}
@override
DartType? visitTypedefType(TypedefType node) {
// NonNull(C<T1, ... , Tn>) = C<T1, ... , Tn> for class C other
// than Null (including Object).
//
// NonNull(T?) = NonNull(T)
//
// NonNull(T*) = NonNull(T)
if (node.declaredNullability == Nullability.nonNullable) {
return null;
}
return node.withDeclaredNullability(Nullability.nonNullable);
}
@override
DartType? visitVoidType(VoidType node) {
// NonNull(void) = void
return null;
}
}