From 7bbd8ea29da52f87f8d85e69ebe8b683df0ce7d4 Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Thu, 23 May 2019 17:15:21 +0000 Subject: [PATCH] Introduce a representation of the type `Never?`. This type is equivalent to the existing type `Null`, but we need it anyway because `Null` can only be accessed via the type provider, and there are circumstances where we need to create this type and don't have access to the type provider. It also may prove beneficial to be able to distinguish between `Null` and `Never?` in diagnostic messages. Change-Id: I6118e87c8c4736a508a3f366f2762a96198db7ca Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103540 Reviewed-by: Konstantin Shcheglov Reviewed-by: Mike Fairhurst Reviewed-by: Brian Wilkerson --- pkg/analyzer/lib/src/dart/element/type.dart | 46 ++++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart index 81f079e0130..57c16379a31 100644 --- a/pkg/analyzer/lib/src/dart/element/type.dart +++ b/pkg/analyzer/lib/src/dart/element/type.dart @@ -56,14 +56,31 @@ typedef List TypeArgumentsComputer(); */ class BottomTypeImpl extends TypeImpl { /** - * The unique instance of this class. + * The unique instance of this class, nullable. + * + * This behaves equivalently to the `Null` type, but we distinguish it for two + * reasons: (1) there are circumstances where we need access to this type, but + * we don't have access to the type provider, so using `Never?` is a + * convenient solution. (2) we may decide that the distinction is convenient + * in diagnostic messages (this is TBD). */ - static final BottomTypeImpl instance = new BottomTypeImpl._(); + static final BottomTypeImpl instanceNullable = + new BottomTypeImpl._(NullabilitySuffix.question); + + /** + * The unique instance of this class, non-nullable. + */ + static final BottomTypeImpl instance = + new BottomTypeImpl._(NullabilitySuffix.none); + + @override + final NullabilitySuffix nullabilitySuffix; /** * Prevent the creation of instances of this class. */ - BottomTypeImpl._() : super(new NeverElementImpl(), "Never") { + BottomTypeImpl._(this.nullabilitySuffix) + : super(new NeverElementImpl(), "Never") { (element as NeverElementImpl).type = this; } @@ -74,7 +91,10 @@ class BottomTypeImpl extends TypeImpl { bool get isBottom => true; @override - NullabilitySuffix get nullabilitySuffix => NullabilitySuffix.none; + bool get isDartCoreNull { + // `Never?` is equivalent to `Null`, so make sure it behaves the same. + return nullabilitySuffix == NullabilitySuffix.question; + } @override bool operator ==(Object object) => identical(object, this); @@ -119,8 +139,22 @@ class BottomTypeImpl extends TypeImpl { @override TypeImpl withNullability(NullabilitySuffix nullabilitySuffix) { - // The bottom type is always non-nullable. - return this; + switch (nullabilitySuffix) { + case NullabilitySuffix.question: + return instanceNullable; + case NullabilitySuffix.star: + // This should never happen. Converting `Never` to a legacy type should + // yield `Null`, because prior to NNBD, `Null` was at the bottom of the + // type hierarchy. + // + // However, due to bugs elsewhere in the analyzer, this does still + // happen sometimes, so for now just coerce to `Never?`. + // TODO(paulberry): change this to throw an exception. + return instanceNullable; + case NullabilitySuffix.none: + return instance; + } + throw StateError('Unexpected nullabilitySuffix: $nullabilitySuffix'); } }