diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index c8358c24704c..5f592bfa9d51 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -127,8 +127,6 @@ object SbtCommunityProject: def scalacOptions = List( "-Xcheck-macros", "-Wsafe-init", - "-Yexplicit-nulls", - "-language:unsafeNulls", ) object projects: diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 2fdf622325b0..5d3e9d81a543 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -615,7 +615,7 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { .setTyper(new Typer) .addMode(Mode.ImplicitsEnabled) .setTyperState(ctx.typerState.fresh(ctx.reporter)) - if ctx.settings.YexplicitNulls.value && !Feature.enabledBySetting(nme.unsafeNulls) then + if ctx.settings.YexplicitNulls.value || Feature.enabledBySetting(nme.safeNulls) then start = start.addMode(Mode.SafeNulls) ctx.initialize()(using start) // re-initialize the base context with start diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index cff1e5b2d662..a6ba7553e1e2 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -1995,8 +1995,8 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => actual match - case actual: FlexibleType => - return actual.derivedFlexibleType(recur(actual.hi, expected, covariant)) + case actual @ FlexibleType(hi) => + return FlexibleType.derivedFlexibleType(actual, recur(hi, expected, covariant)) case _ => // Decompose the actual type into the inner shape type, the capture set and the box status diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index f5e0502e1689..22dca48f5f77 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -62,6 +62,7 @@ object Feature: (nme.noAutoTupling, "Disable automatic tupling"), (nme.dynamics, "Allow direct or indirect subclasses of scala.Dynamic"), (nme.unsafeNulls, "Enable unsafe nulls for explicit nulls"), + (nme.safeNulls, "Enable safe nulls for explicit nulls"), (nme.postfixOps, "Allow postfix operators (not recommended)"), (nme.strictEquality, "Enable strict equality (disable canEqualAny)"), (nme.implicitConversions, "Allow implicit conversions without warnings"), diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index fae6cf5dacd4..8fcfc3a85875 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -561,7 +561,8 @@ private sealed trait YSettings: val YmagicOffsetHeader: Setting[String] = StringSetting(ForkSetting, "Ymagic-offset-header", "header", "Specify the magic header comment that marks the start of the actual code in generated wrapper scripts. Example: -Ymagic-offset-header:SOURCE_CODE_START. Then, in the source, the magic comment `///SOURCE_CODE_START:` marks the start of user code. The comment should be suffixed by `:` to indicate the original file.", "") // Experimental language features - val YexplicitNulls: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.") + val YexplicitNulls: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplicit-nulls", "Since explicit nulls is enabled by default, this flag now enables safe nulls for explicit-nulls") + val YnoExplicitNulls: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-explicit-nulls", "Make reference types implictly nullable.") val YnoFlexibleTypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-flexible-types", "Disable turning nullable Java return types and parameter types into flexible types, which behave like abstract types with a nullable lower bound and non-nullable upper bound.") val YflexifyTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Yflexify-tasty", "Apply flexification to Scala code compiled without -Yexplicit-nulls, when reading from tasty.") val YsafeInitGlobal: Setting[Boolean] = BooleanSetting(ForkSetting, "Ysafe-init-global", "Check safe initialization of global objects.") @@ -577,6 +578,7 @@ private sealed trait YSettings: val YexplainLowlevel: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplain-lowlevel", "When explaining type errors, show types at a lower level.") val YnoDoubleBindings: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-double-bindings", "Assert no namedtype is bound twice (should be enabled only if program is error-free).") val YshowVarBounds: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-var-bounds", "Print type variables with their bounds.") + val YhideFlexibleTypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yhide-flexible-types", "Print flexible types as their base type. (T instead of (T)?)") val Yinstrument: Setting[Boolean] = BooleanSetting(ForkSetting, "Yinstrument", "Add instrumentation code that counts allocations and closure creations.") val YinstrumentDefs: Setting[Boolean] = BooleanSetting(ForkSetting, "Yinstrument-defs", "Add instrumentation code that counts method calls; needs -Yinstrument to be set, too.") diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 82ebd6a4e331..3a8855be8c10 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -720,8 +720,8 @@ trait ConstraintHandling { tp.rebind(tp.parent.hardenUnions) case tp: HKTypeLambda => tp.derivedLambdaType(resType = tp.resType.hardenUnions) - case tp: FlexibleType => - tp.derivedFlexibleType(tp.hi.hardenUnions) + case tp @ FlexibleType(hi) => + FlexibleType.derivedFlexibleType(tp, hi.hardenUnions) case tp: OrType => val tp1 = tp.stripNull(stripFlexibleTypes = false) if tp1 ne tp then tp.derivedOrType(tp1.hardenUnions, defn.NullType, soft = false) @@ -832,6 +832,14 @@ trait ConstraintHandling { */ protected def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean)(using Context): Boolean = if !bound.isValueTypeOrLambda then return false + // Never infer the `` type constructor for a higher-kinded type + // parameter. A flexible type is an implementation device of explicit nulls that + // should be transparent to type inference; if we allowed `param := `, + // every `F[A]`-shaped signature would match a flexible-typed value, shadowing the + // members of its underlying type (see tests/explicit-nulls/pos/flexible-hk-extension.scala). + // Refusing the constraint makes the comparison fall back to looking through the + // flexible type. + if FlexibleType.isTypeConstructor(bound) then return false /** When comparing lambdas we might get constraints such as * `A <: X0` or `A = List[X0]` where `A` is a constrained parameter diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index eefc22011fe7..6e548ffe1c72 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -489,13 +489,13 @@ object Contexts { fresh.setSetting(ctx.settings.color, "never") /** Is the explicit nulls option set? */ - def explicitNulls: Boolean = base.settings.YexplicitNulls.value + def explicitNulls: Boolean = !base.settings.YnoExplicitNulls.value /** Is the flexible types option set? */ - def flexibleTypes: Boolean = base.settings.YexplicitNulls.value && !base.settings.YnoFlexibleTypes.value + def flexibleTypes: Boolean = explicitNulls && !base.settings.YnoFlexibleTypes.value /** Is the flexify tasty option set? */ - def flexifyTasty: Boolean = base.settings.YexplicitNulls.value && base.settings.YflexifyTasty.value + def flexifyTasty: Boolean = explicitNulls && base.settings.YflexifyTasty.value /** Is the best-effort option set? */ def isBestEffort: Boolean = base.settings.YbestEffort.value @@ -771,7 +771,9 @@ object Contexts { importInfo.mentionsFeature(nme.unsafeNulls) match case Some(true) => setMode(this.mode &~ Mode.SafeNulls) - case Some(false) if ctx.settings.YexplicitNulls.value => + case _ => + importInfo.mentionsFeature(nme.safeNulls) match + case Some(true) if explicitNulls => setMode(this.mode | Mode.SafeNulls) case _ => updateStore(importInfoLoc, importInfo) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 74c95a9368e4..017266ee137f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -449,6 +449,17 @@ class Definitions { newPermanentSymbol(OpsPackageClass, tpnme.FromJavaObject, JavaDefined, TypeAlias(ObjectType)).entered def FromJavaObjectType: TypeRef = FromJavaObjectSymbol.typeRef + @tu lazy val FlexibleTypeSymbol: TypeSymbol = + newPermanentSymbol(ScalaPackageClass, tpnme.FlexibleType, EmptyFlags, TypeBounds( + HKTypeLambda(TypeBounds.empty :: Nil)( + tl => OrNull(tl.paramRefs(0)) + ), + HKTypeLambda(TypeBounds.empty :: Nil)( + tl => tl.paramRefs(0) + ) + )).entered + def FlexibleTypeType: TypeRef = FlexibleTypeSymbol.typeRef + @tu lazy val AnyRefAlias: TypeSymbol = enterAliasType(tpnme.AnyRef, ObjectType) def AnyRefType: TypeRef = AnyRefAlias.typeRef @@ -2022,8 +2033,8 @@ class Definitions { asContextFunctionType(TypeComparer.bounds(tp1).hiBound) case tp1 @ PolyFunctionOf(mt: MethodType) if mt.isContextualMethod => tp1 - case tp: FlexibleType => - asContextFunctionType(tp.hi) + case FlexibleType(hi) => + asContextFunctionType(hi) case tp1 => if tp1.typeSymbol.name.isContextFunction && isFunctionNType(tp1) then tp1 else NoType diff --git a/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala b/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala index 7350af348ea0..d94324d6ee57 100644 --- a/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala +++ b/compiler/src/dotty/tools/dotc/core/ImplicitNullInterop.scala @@ -192,12 +192,18 @@ object ImplicitNullInterop: state = savedState if isNullAnnot then parent2 else parent2 match - case FlexibleType(_, parent2a) => + case FlexibleType(parent2a) => FlexibleType(derivedAnnotatedType(tp, parent2a, tp.annot)) case OrNull(parent2a) => OrNull(derivedAnnotatedType(tp, parent2a, tp.annot)) case _ => derivedAnnotatedType(tp, parent2, tp.annot) + case FlexibleType(_) => + // A flexible type is already a fully-nullified result, so nullification must be + // idempotent on it: leave it unchanged. Without this case it would match the + // `AppliedType` case below (a flexible type is encoded as `AppliedType(FlexibleType, T)`) + // and get wrapped a second time, e.g. `(Array[T])?` becoming `((Array[T])?)?`. + tp case appTp @ AppliedType(tycon, targs) => val savedState = state // If Java-defined tycon, don't nullify outer level of type args (Java classes are fully nullified) @@ -236,7 +242,7 @@ object ImplicitNullInterop: // This keeps the result minimal and avoids duplicating `| Null` // on both sides and at the outer level. (this(tp.tp1), this(tp.tp2)) match - case (FlexibleType(_, t1), FlexibleType(_, t2)) if ctx.flexibleTypes => + case (FlexibleType(t1), FlexibleType(t2)) if ctx.flexibleTypes => FlexibleType(derivedAndOrType(tp, t1, t2)) case (OrNull(t1), OrNull(t2)) => OrNull(derivedAndOrType(tp, t1, t2)) @@ -256,7 +262,7 @@ object ImplicitNullInterop: // If the parent type becomes nullable, then we pop the nullification to the outer level. parent2 match - case FlexibleType(_, parent2a) => + case FlexibleType(parent2a) => FlexibleType(derivedRefinedType(tp, parent2a, refinedInfo2)) case OrNull(parent2a) => OrNull(derivedRefinedType(tp, parent2a, refinedInfo2)) diff --git a/compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala b/compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala index 572c94070066..ce066f05d119 100644 --- a/compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala +++ b/compiler/src/dotty/tools/dotc/core/NullOpsDecorator.scala @@ -33,9 +33,9 @@ object NullOpsDecorator: if (tp1s ne tp1) && (tp2s ne tp2) then tp.derivedAndType(tp1s, tp2s) else tp - case tp: FlexibleType => - val hi1 = strip(tp.hi) - if stripFlexibleTypes then hi1 else tp.derivedFlexibleType(hi1) + case tp @ FlexibleType(hi) => + val hi1 = strip(hi) + if stripFlexibleTypes then hi1 else FlexibleType.derivedFlexibleType(tp, hi1) case tp @ TypeBounds(lo, hi) => tp.derivedTypeBounds(strip(lo), strip(hi)) case tp => tp diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 5017f9489e46..d03b4ca6eacc 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -557,8 +557,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, if underlying1 ne tp.underlying then underlying1 else tp case CapturingType(parent, refs) => tp.derivedCapturingType(recur(parent), refs) - case tp: FlexibleType => - tp.derivedFlexibleType(recur(tp.hi)) + case tp @ FlexibleType(hi) => + FlexibleType.derivedFlexibleType(tp, recur(hi)) case tp: AnnotatedType => tp.derivedAnnotatedType(recur(tp.parent), tp.annot) case _ => @@ -754,7 +754,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, case tp: TypeVar if contains(tp.origin) => withHard(tp) case tp: TypeParamRef if contains(tp) => hardenTypeVars(typeVarOfParam(tp)) case tp: AndOrType => hardenTypeVars(tp.tp1).hardenTypeVars(tp.tp2) - case tp: FlexibleType => hardenTypeVars(tp.hi) + case FlexibleType(hi) => hardenTypeVars(hi) case _ => this def remove(pt: TypeLambda)(using Context): This = { diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 9baf0c40a80b..5500fdfbedea 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -167,7 +167,7 @@ trait PatternTypeConstrainer { self: TypeComparer => // additional trait - argument-less enum cases desugar to vals. // See run/enum-Tree.scala. if tp.classSymbol.exists then tp else tp.info - case tp: FlexibleType => dealiasDropNonmoduleRefs(tp.underlying) + case FlexibleType(hi) => dealiasDropNonmoduleRefs(hi) case tp => tp } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 67000ec5db27..95e66df8671c 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -206,6 +206,7 @@ object StdNames { final val NotNull: N = "NotNull" final val Null: N = "Null" final val Object: N = "Object" + final val FlexibleType : N = "" final val FromJavaObject: N = "" final val Record: N = "Record" final val Product: N = "Product" @@ -667,6 +668,7 @@ object StdNames { val unbox: N = "unbox" val universe: N = "universe" val unsafeNulls: N = "unsafeNulls" + val safeNulls: N = "safeNulls" val update: N = "update" val updateDynamic: N = "updateDynamic" val uses: N = "uses" diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index a3d8ed6c4328..0cb6b808905e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -589,8 +589,8 @@ class TypeApplications(val self: Type) extends AnyVal { * Existential types in arguments are returned as TypeBounds instances. */ final def argInfos(using Context): List[Type] = self.stripped match + case FlexibleType(hi) => hi.argInfos case AppliedType(tycon, args) => args - case tp: FlexibleType => tp.underlying.argInfos case _ => Nil /** If this is an encoding of a function type, return its arguments, otherwise return Nil. diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 7776d82ee033..f385ce23e3de 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -950,8 +950,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling false } compareClassInfo - case tp2: FlexibleType => - recur(tp1, tp2.lo) case _ => fourthTry } @@ -1160,8 +1158,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tp1: ExprType if ctx.phaseId > gettersPhase.id => // getters might have converted T to => T, need to compensate. recur(tp1.widenExpr, tp2) - case tp1: FlexibleType => - recur(tp1.hi, tp2) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 24bc84ba9e40..b01c2184082b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -318,7 +318,7 @@ object Types extends TypeUtils { isRef(defn.ObjectClass) && (typeSymbol eq defn.FromJavaObjectSymbol) def containsFromJavaObject(using Context): Boolean = this match - case tp: FlexibleType => tp.underlying.containsFromJavaObject + case FlexibleType(hi) => hi.containsFromJavaObject case tp: OrType => tp.tp1.containsFromJavaObject || tp.tp2.containsFromJavaObject case tp: AndType => tp.tp1.containsFromJavaObject && tp.tp2.containsFromJavaObject case _ => isFromJavaObject @@ -383,7 +383,7 @@ object Types extends TypeUtils { /** Is this type guaranteed not to have `null` as a value? */ final def isNotNull(using Context): Boolean = this match { case tp: ConstantType => tp.value.value != null - case tp: FlexibleType => false + case FlexibleType(_) => false case tp: ThisType => true case tp: SuperType => true case tp: ClassInfo => !tp.cls.isNullableClass && !tp.isNothingType @@ -401,7 +401,7 @@ object Types extends TypeUtils { case OrType(l, r) => r.admitsNull || l.admitsNull case AndType(l, r) => r.admitsNull && l.admitsNull case TypeBounds(lo, hi) => lo.admitsNull - case FlexibleType(lo, hi) => true + case FlexibleType(_) => true case tp: TypeProxy => tp.underlying.admitsNull case _ => false ) @@ -427,7 +427,6 @@ object Types extends TypeUtils { case AppliedType(tycon, args) => tycon.unusableForInference || args.exists(_.unusableForInference) case RefinedType(parent, _, rinfo) => parent.unusableForInference || rinfo.unusableForInference case TypeBounds(lo, hi) => lo.unusableForInference || hi.unusableForInference - case tp: FlexibleType => tp.underlying.unusableForInference case tp: AndOrType => tp.tp1.unusableForInference || tp.tp2.unusableForInference case tp: LambdaType => tp.resultType.unusableForInference || tp.paramInfos.exists(_.unusableForInference) case WildcardType(optBounds) => optBounds.unusableForInference @@ -467,8 +466,9 @@ object Types extends TypeUtils { (new isGroundAccumulator).apply(true, this) /** Is this a type of a repeated parameter? */ - def isRepeatedParam(using Context): Boolean = - typeSymbol eq defn.RepeatedParamClass + def isRepeatedParam(using Context): Boolean = this match + case FlexibleType(hi) => hi.isRepeatedParam + case _ => typeSymbol eq defn.RepeatedParamClass /** Is this type of the form `compiletime.into[T]`, which means it can be the * target of an implicit converson without requiring a language import? @@ -1474,8 +1474,8 @@ object Types extends TypeUtils { tp.rebind(tp.parent.widenUnion) case tp: HKTypeLambda => tp.derivedLambdaType(resType = tp.resType.widenUnion) - case tp: FlexibleType => - tp.derivedFlexibleType(tp.hi.widenUnionWithoutNull) + case tp @ FlexibleType(hi) => + FlexibleType.derivedFlexibleType(tp, hi.widenUnionWithoutNull) case tp => tp @@ -1905,8 +1905,6 @@ object Types extends TypeUtils { t case t @ SAMType(_, _) => t - case ft: FlexibleType => - ft.underlying.findFunctionType case _ => NoType @@ -3452,42 +3450,38 @@ object Types extends TypeUtils { * `T | Null .. T`, so that `T | Null <: FlexibleType(T) <: T`. * A flexible type will be erased to its original type `T`. */ - case class FlexibleType protected(lo: Type, hi: Type) extends CachedProxyType with ValueType { - override def underlying(using Context): Type = hi + object FlexibleType: + def apply(tp: Type)(using Context): Type = + assert(tp.isValueType, s"Should not flexify ${tp}") + tp match + case ft @ FlexibleType(hi) => ft + case _ => AppliedType(defn.FlexibleTypeType, tp :: Nil) - def derivedFlexibleType(hi: Type)(using Context): Type = - if hi eq this.hi then this else FlexibleType.make(hi) + def unapply(tp: AppliedType)(using Context): Option[Type] = + if tp.tycon.isRef(defn.FlexibleTypeSymbol) then Some(tp.args.head) + else None - override def computeHash(bs: Binders): Int = doHash(bs, hi) + def isInstance(tp: Type)(using Context): Boolean = tp match + case FlexibleType(_) => true + case _ => false - override final def baseClasses(using Context): List[ClassSymbol] = hi.baseClasses - } + /** Is `tp` the `` type constructor itself (possibly eta-expanded)? + * Such a type is an implementation device of explicit nulls; it must never be + * inferred as the instance of a higher-kinded type parameter. + */ + def isTypeConstructor(tp: Type)(using Context): Boolean = tp.stripTypeVar match + case tp: TypeRef => tp.symbol eq defn.FlexibleTypeSymbol + case tp: HKTypeLambda => isInstance(tp.resType) || isTypeConstructor(tp.resType) + case _ => false - object FlexibleType: - def apply(tp: Type)(using Context): FlexibleType = - assert(tp.isValueType, s"Should not flexify ${tp}") + def derivedFlexibleType(tp: Type, hi: Type)(using Context): Type = tp match - case ft: FlexibleType => ft - case _ => FlexibleType(OrNull(tp), tp) - // val tp1 = tp.stripNull() - // if tp1.isNullType then - // // (Null)? =:= ? >: Null <: (Object & Null) - // FlexibleType(tp, AndType(defn.ObjectType, defn.NullType)) - // else - // // (T | Null)? =:= ? >: T | Null <: T - // // (T)? =:= ? >: T | Null <: T - // val hi = tp1 - // val lo = if hi eq tp then OrNull(hi) else tp - // FlexibleType(lo, hi) - // - // The commented out code does more work to analyze the original type to ensure the - // flexible type is always a subtype of the original type and the Object type. - // It is not necessary according to the use cases, so we choose to use a simpler - // rule. + case FlexibleType(hi0) if hi eq hi0 => tp + case _ => FlexibleType.make(hi) def make(tp: Type)(using Context): Type = tp match - case _: FlexibleType => tp // tp is already flexible + case tp @ FlexibleType(hi) => tp // tp is already flexible case SimpleOrNull(_) => tp // tp is already nullable case TypeBounds(lo, hi) => TypeBounds(FlexibleType.make(lo), FlexibleType.make(hi)) case wt: WildcardType => wt.optBounds match @@ -6060,8 +6054,6 @@ object Types extends TypeUtils { samClass(tp.underlying) case tp: AnnotatedType => samClass(tp.underlying) - case tp: FlexibleType => - samClass(tp.underlying) case _ => NoSymbol @@ -6219,8 +6211,6 @@ object Types extends TypeUtils { tp.derivedJavaArrayType(elemtp) protected def derivedExprType(tp: ExprType, restpe: Type): Type = tp.derivedExprType(restpe) - protected def derivedFlexibleType(tp: FlexibleType, hi: Type): Type = - tp.derivedFlexibleType(hi) // note: currying needed because Scala2 does not support param-dependencies protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = tp.derivedLambdaType(tp.paramNames, formals, restpe) @@ -6416,9 +6406,6 @@ object Types extends TypeUtils { case tp: OrType => derivedOrType(tp, this(tp.tp1), this(tp.tp2)) - case tp: FlexibleType => - derivedFlexibleType(tp, this(tp.hi)) - case tp: MatchType => val bound1 = this(tp.bound) val scrut1 = atVariance(0)(this(tp.scrutinee)) @@ -6663,6 +6650,16 @@ object Types extends TypeUtils { override protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = tycon match { + case tr if tr.isRef(defn.FlexibleTypeSymbol) => + val hi = args.head + hi match { + case Range(lo, hi) => + // We know FlexibleType(t).hi = t and FlexibleType(t).lo = OrNull(t) + range(OrNull(lo), hi) + case _ => + if (hi.isExactlyNothing) hi + else FlexibleType.derivedFlexibleType(tp, hi) + } case Range(tyconLo, tyconHi) => range(derivedAppliedType(tp, tyconLo, args), derivedAppliedType(tp, tyconHi, args)) case _ => @@ -6730,16 +6727,6 @@ object Types extends TypeUtils { else tp.derivedAnnotatedType(underlying, annot) } - override protected def derivedFlexibleType(tp: FlexibleType, hi: Type): Type = - hi match { - case Range(lo, hi) => - // We know FlexibleType(t).hi = t and FlexibleType(t).lo = OrNull(t) - range(OrNull(lo), hi) - case _ => - if (hi.isExactlyNothing) hi - else tp.derivedFlexibleType(hi) - } - override protected def derivedCapturingType(tp: Type, parent: Type, refs: CaptureSet): Type = parent match // TODO ^^^ handle ranges in capture sets as well case Range(lo, hi) => @@ -6879,9 +6866,6 @@ object Types extends TypeUtils { case tp: TypeVar => this(x, tp.underlying) - case tp: FlexibleType => - this(x, tp.underlying) - case ExprType(restpe) => this(x, restpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a5232efd6480..dad289a40e00 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -187,6 +187,9 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { } private def pickleNewType(tpe: Type, richTypes: Boolean)(using Context): Unit = tpe match { + case FlexibleType(hi) => + writeByte(FLEXIBLEtype) + withLength { pickleType(hi, richTypes) } case AppliedType(tycon, args) => if tycon.typeSymbol == defn.MatchCaseClass then writeByte(MATCHCASEtype) @@ -294,9 +297,6 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { case tpe: OrType => writeByte(ORtype) withLength { pickleType(tpe.tp1, richTypes); pickleType(tpe.tp2, richTypes) } - case tpe: FlexibleType => - writeByte(FLEXIBLEtype) - withLength { pickleType(tpe.underlying, richTypes) } case tpe: ExprType => writeByte(BYNAMEtype) pickleType(tpe.underlying) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 16cc83bc13b0..5ce2cec8c541 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -420,7 +420,10 @@ class TreeUnpickler(reader: TastyReader, if nothingButMods(end) then AliasingBounds(readVariances(lo)) else val hi = readVariances(readType()) - createNullableTypeBounds(lo, hi) + if (ctx.flexifyTasty && !explicitNulls) + createNullableTypeBounds(lo, hi) + else + TypeBounds(lo, hi) case ANNOTATEDtype => val parent = readType() val ann = @@ -1716,7 +1719,10 @@ class TreeUnpickler(reader: TastyReader, val lo = readTpt() val hi = if currentAddr == end then lo else readTpt() val alias = if currentAddr == end then EmptyTree else readTpt() - createNullableTypeBoundsTree(lo, hi, alias) + if (ctx.flexifyTasty && !explicitNulls) + createNullableTypeBoundsTree(lo, hi, alias) + else + TypeBoundsTree(lo, hi, alias) case QUOTE => Quote(readTree(), Nil).withBodyType(readType()) case SPLICE => diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 5d079007d92d..161d54169741 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -243,6 +243,11 @@ class PlainPrinter(_ctx: Context) extends Printer { ParamRefNameString(tp) ~ hashStr(tp.binder) ~ suffix case tp: SingletonType => toTextSingleton(tp) + case FlexibleType(tpe) => + if (ctx.settings.YhideFlexibleTypes.value) then + toText(tpe) + else + "(" ~ toText(tpe) ~ ")?" case AppliedType(tycon, args) => (toTextLocal(tycon) ~ "[" ~ argsText(args) ~ "]").close case tp: RefinedType => @@ -332,8 +337,6 @@ class PlainPrinter(_ctx: Context) extends Printer { toText(tpe) case _ => toTextLocal(tpe) ~ " " ~ toText(annot) - case FlexibleType(_, tpe) => - "(" ~ toText(tpe) ~ ")?" case tp: TypeVar => def toTextCaret(tp: Type) = if printDebug then toTextLocal(tp) ~ Str("^") else toText(tp) if (tp.isInstantiated) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 31f2b2b8811e..e73162f519e6 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -299,6 +299,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { Str("") homogenize(tp) match { + case tp @ FlexibleType(_) => + super.toText(tp) case tp: AppliedType => val refined = appliedText(tp) if refined.isEmpty then super.toText(tp) else refined diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index d12eb7c5cfe9..ec587798e009 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -548,6 +548,8 @@ private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol]) else tp.prefix api.Projection.of(apiType(prefix), sym.name.toString) + case FlexibleType(hi) => + apiType(hi) case AppliedType(tycon, args) => def processArg(arg: Type): api.Type = arg match { case arg @ TypeBounds(lo, hi) => // Handle wildcard parameters @@ -627,8 +629,6 @@ private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol]) case tp: OrType => val s = combineApiTypes(apiType(tp.tp1), apiType(tp.tp2)) withMarker(s, orMarker) - case tp: FlexibleType => - apiType(tp.underlying) case ExprType(resultType) => withMarker(apiType(resultType), byNameMarker) case MatchType(bound, scrut, cases) => diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index 4dc9dfa5b22f..df816abde8aa 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -438,7 +438,7 @@ class Pickler extends Phase { val attributes = Attributes( sourceFile = unit.source.pathRelativeToSourceRoot, scala2StandardLibrary = Feature.shouldBehaveAsScala2, - explicitNulls = ctx.settings.YexplicitNulls.value, + explicitNulls = ctx.explicitNulls, captureChecked = Feature.ccEnabled, withPureFuns = Feature.pureFunsEnabled, isJava = isJavaAttr, diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 54b9410a4bc8..db65cd832856 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -151,8 +151,8 @@ object TypeTestsCasts { // - T1 & T2 <:< T3 // See TypeComparer#either recur(tp1, P) && recur(tp2, P) - case tpX: FlexibleType => - recur(tpX.underlying, P) + case FlexibleType(hi) => + recur(hi, P) case x => // always false test warnings are emitted elsewhere // provablyDisjoint wants fully applied types as input; because we're in the middle of erasure, we sometimes get raw types here diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 961d79cb2777..c444c4afdcfd 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -971,7 +971,7 @@ object SpaceEngine { case tp: SingletonType => toUnderlying(tp.underlying) case tp: ExprType => toUnderlying(tp.resultType) case AnnotatedType(tp, annot) => AnnotatedType(toUnderlying(tp), annot) - case tp: FlexibleType => tp.derivedFlexibleType(toUnderlying(tp.underlying)) + case tp @ FlexibleType(hi) => FlexibleType.derivedFlexibleType(tp, toUnderlying(hi)) case _ => tp }) @@ -1115,7 +1115,7 @@ object SpaceEngine { def checkReachability(m: Match)(using Context): Unit = trace(i"checkReachability($m)"): val selTyp = toUnderlying(m.selector.tpe).dealias - val isNullable = selTyp.isInstanceOf[FlexibleType] || selTyp.classSymbol.isNullableClass + val isNullable = FlexibleType.isInstance(selTyp) || selTyp.classSymbol.isNullableClass val targetSpace = trace(i"targetSpace($selTyp)"): if isNullable && !ctx.mode.is(Mode.SafeNulls) then project(OrType(selTyp, ConstantType(Constant(null)), soft = false)) @@ -1144,7 +1144,7 @@ object SpaceEngine { if isSubspace(covered, prev) then report.warning(MatchCaseUnreachable(), pat.srcPos) else if isNullable - && (!ctx.mode.is(Mode.SafeNulls) || selTyp.isInstanceOf[FlexibleType]) + && (!ctx.mode.is(Mode.SafeNulls) || FlexibleType.isInstance(selTyp)) && isWildcardArg(pat) && isSubspace(covered, Or(prev :: nullSpace :: Nil)) && !hadNullOnly diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a87c8efe8573..d5f484c145f6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1013,9 +1013,9 @@ trait Applications extends Compatibility { // However, for overload resolution, we want to check applicability: // "could this work with some type instantiation?" (yes, if ? = String) def wildcardArgOK = - argtpe match + argtpe.stripNull() match case at @ AppliedType(tycon1, args1) if at.hasWildcardArg => - formal match + formal.stripNull() match case AppliedType(tycon2, args2) if tycon1 =:= tycon2 && args1.length == args2.length => // We need to handle all 4 cases, in addition to diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 04ac34c1a12e..f84368bb5b1a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1047,6 +1047,13 @@ trait Implicits: // This is done to check whether such types might plausibly be comparable to each other. val lift = new TypeMap { def apply(t: Type): Type = t match { + case FlexibleType(hi) => + // Keep the flexible wrapper (it admits null), but lift the underlying + // type to its upper bound like any other abstract type. Mapping the + // flexible type with `mapOver` instead would lift its tycon to the + // tycon's upper bound and collapse the flexible type entirely, losing + // the nullability information. + FlexibleType.derivedFlexibleType(t, apply(hi)) case t: TypeRef => t.info match { case TypeBounds(lo, hi) if lo.ne(hi) && !t.symbol.is(Opaque) => apply(hi) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index e899aa06bb0f..3e42a7e8fd90 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -619,13 +619,13 @@ object Inferencing { case tp: RecType => tp.derivedRecType(captureWildcards(tp.parent)) case tp: LazyRef => captureWildcards(tp.ref) case tp: AnnotatedType => tp.derivedAnnotatedType(captureWildcards(tp.parent), tp.annot) - case tp: FlexibleType => tp.derivedFlexibleType(captureWildcards(tp.hi)) + case tp @ FlexibleType(hi) => FlexibleType.derivedFlexibleType(tp, captureWildcards(hi)) case _ => tp } def hasCaptureConversionArg(tp: Type)(using Context): Boolean = tp match + case FlexibleType(hi) => hasCaptureConversionArg(hi) case tp: AppliedType => tp.args.exists(_.typeSymbol == defn.TypeBox_CAP) - case tp: FlexibleType => hasCaptureConversionArg(tp.hi) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index e58be92954a9..48ad8ddf2bea 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1238,9 +1238,10 @@ object RefChecks { if ctx.explicitNulls && !ctx.isJava && sym.exists && sym.owner.isClass && !sym.owner.isAnonymousClass + && !sym.owner.name.isReplWrapperName && !sym.isOneOf(JavaOrPrivateOrSynthetic | InlineProxy | Param | Exported) then val resTp = sym.info.finalResultType - if resTp.existsPart(_.isInstanceOf[FlexibleType], StopAt.Static) then + if resTp.existsPart(FlexibleType.isInstance(_), StopAt.Static) then report.warning( em"${sym.show} exposes a flexible type in its inferred result type ${resTp}. Consider annotating the type explicitly", sym.srcPos diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b1b8c1ed08ae..39ce6c36cc92 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1358,8 +1358,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (untpd.isWildcardStarArg(tree)) { def fromRepeated(pt: Type): Type = pt match - case pt: FlexibleType => - pt.derivedFlexibleType(fromRepeated(pt.hi)) + case pt @ FlexibleType(hi) => + FlexibleType.derivedFlexibleType(pt, fromRepeated(hi)) case _ => if ctx.mode.isQuotedPattern then // FIXME(#8680): Quoted patterns do not support Array repeated arguments @@ -1684,8 +1684,21 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val result = if tree.elsep.isEmpty then - val thenp1 = typed(tree.thenp, branchPt)(using cond1.nullableContextIf(true)) + val thenp0 = typed(tree.thenp, branchPt)(using cond1.nullableContextIf(true)) val elsep1 = tpd.unitLiteral.withSpan(tree.span.endPos) + // Discard a `then` value that only *conforms* to `Unit` (e.g. a Java + // `FlexibleType[Unit]`, as returned by `Map[K, Unit].put`) so the branch is + // actually `Unit`. Otherwise `assignType(If)`'s lub (used when the tree is + // unpickled or rebuilt) recomputes the `if` to `lub(FlexibleType[Unit], Unit) = + // FlexibleType[Unit]` and disagrees with the `Unit` hardcoded here, breaking TASTY + // pickling round-trips. (The previous `FlexibleType` representation hid this: being + // a freshly-allocated proxy rather than a hash-consed `AppliedType`, it failed the + // `eq` check in `TypedTreeCopier.If`, forcing that copier to recompute the `if` to + // the same lub the unpickler uses.) + val thenp1 = + if FlexibleType.isInstance(thenp0.tpe.widenExpr) + then tpd.Block(thenp0 :: Nil, tpd.unitLiteral.withSpan(tree.span.endPos)) + else thenp0 cpy.If(tree)(cond1, thenp1, elsep1).withType(defn.UnitType) else val thenp1 :: elsep1 :: Nil = harmonic(harmonize, pt) { @@ -1999,8 +2012,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val t1 = instantiatableTypeVar(tp.tp1) if t1.exists then t1 else instantiatableTypeVar(tp.tp2) - case tp: FlexibleType => - instantiatableTypeVar(tp.hi) + case FlexibleType(hi) => + instantiatableTypeVar(hi) case tp: TypeVar if isConstrainedByFunctionType(tp) => // Only instantiate if the type variable is constrained by function types tp @@ -2015,8 +2028,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case SAMType(_, _) => true case tp: AndOrType => containsFunctionType(tp.tp1) || containsFunctionType(tp.tp2) - case tp: FlexibleType => - containsFunctionType(tp.hi) case _ => false containsFunctionType(bounds.lo) || containsFunctionType(bounds.hi) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index bbc6f117b58e..555b628fcdf9 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1931,7 +1931,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler member.info.substThis(self.classSymbol.asClass, self) else member.info - + // We treat the constructor type parameters as if they were the same as corresponding type members. // That's how Scala 2 symbols are unpickled to begin with. val memberInfoSubstituted = @@ -1981,9 +1981,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler self.subst(from, to) def typeArgs: List[TypeRepr] = self match + case FlexibleType(tp) => tp.typeArgs case AppliedType(_, args) => args case AnnotatedType(parent, _) => parent.typeArgs - case FlexibleType(underlying) => underlying.typeArgs case _ => List.empty end extension end TypeReprMethods @@ -2473,24 +2473,28 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def unapply(x: NoPrefix): true = true end NoPrefix - type FlexibleType = dotc.core.Types.FlexibleType + // A flexible type is represented as an `AppliedType` over the synthetic + // `` symbol. We use an opaque type so that `FlexibleType` is + // nominally distinct from `AppliedType`, otherwise the two `TypeTest`s would + // be indistinguishable and `case FlexibleType(_)` could match plain applied types. + opaque type FlexibleType <: TypeRepr = dotc.core.Types.AppliedType object FlexibleTypeTypeTest extends TypeTest[TypeRepr, FlexibleType]: def unapply(x: TypeRepr): Option[FlexibleType & x.type] = x match - case x: (Types.FlexibleType & x.type) => Some(x) + case x: (Types.AppliedType & x.type) if Types.FlexibleType.isInstance(x) => Some(x) case _ => None end FlexibleTypeTypeTest object FlexibleType extends FlexibleTypeModule: - def apply(tp: TypeRepr): FlexibleType = Types.FlexibleType(tp) - def unapply(x: FlexibleType): Some[TypeRepr] = Some(x.hi) + def apply(tp: TypeRepr): FlexibleType = Types.FlexibleType(tp).asInstanceOf[FlexibleType] + def unapply(x: FlexibleType): Option[TypeRepr] = Types.FlexibleType.unapply(x) end FlexibleType given FlexibleTypeMethods: FlexibleTypeMethods with extension (self: FlexibleType) - def underlying: TypeRepr = self.hi - def lo: TypeRepr = self.lo - def hi: TypeRepr = self.hi + def underlying: TypeRepr = Types.FlexibleType.unapply(self).get + def lo: TypeRepr = Types.OrNull(Types.FlexibleType.unapply(self).get) + def hi: TypeRepr = Types.FlexibleType.unapply(self).get end extension end FlexibleTypeMethods diff --git a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala index b43b6e23e8ca..b4a61ddb8563 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala @@ -214,6 +214,8 @@ object Extractors { this += "TypeRef(" += qual += ", \"" += name += "\")" case Refinement(parent, name, info) => this += "Refinement(" += parent += ", \"" += name += "\", " += info += ")" + case FlexibleType(tp) => + this += "FlexibleType(" += tp += ")" case AppliedType(tycon, args) => this += "AppliedType(" += tycon += ", " ++= args += ")" case AnnotatedType(underlying, annot) => @@ -253,8 +255,6 @@ object Extractors { this += "NoPrefix()" case MatchCase(pat, rhs) => this += "MatchCase(" += pat += ", " += rhs += ")" - case FlexibleType(tp) => - this += "FlexibleType(" += tp += ")" case tp => this += s"" } diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index 4bf59423dd79..4f7ebf262043 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1142,6 +1142,11 @@ object SourceCode { case tpe @ Refinement(_, _, _) => printRefinement(tpe) + case FlexibleType(tp) => + this += "(" + printType(tp) + this += ")?" + case AppliedType(tp, args) => tp match { case tp: TypeLambda => @@ -1260,11 +1265,6 @@ object SourceCode { this += " => " printType(rhs) - case FlexibleType(tp) => - this += "(" - printType(tp) - this += ")?" - case _ => cannotBeShownAsSource(tpe.show(using Printer.TypeReprStructure)) } diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 557eecbb0c17..97279b677d17 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1591,7 +1591,7 @@ class DottyBytecodeTests extends DottyBytecodeTest { assertInvoke(getMethod(c, "f1"), "[Ljava/lang/String;", "clone") // array descriptor as receiver assertInvoke(getMethod(c, "f2"), "java/lang/Object", "hashCode") // object receiver assertInvoke(getMethod(c, "f3"), "java/lang/Object", "hashCode") - assertInvoke(getMethod(c, "f4"), "java/lang/Object", "toString") + assertInvoke(getMethod(c, "f4"), "java/util/Objects", "toString") } } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 700a4c2dbcc3..cc6bdf74b4a0 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -273,7 +273,7 @@ class CompilationTests { val compilationTest = withCoverage(aggregateTests( compileFilesInDir("tests/explicit-nulls/pos", explicitNullsOptions), compileFilesInDir("tests/explicit-nulls/flexible-types-common", explicitNullsOptions), - compileFilesInDir("tests/explicit-nulls/unsafe-common", explicitNullsOptions `and` "-language:unsafeNulls" `and` "-Yno-flexible-types"), + compileFilesInDir("tests/explicit-nulls/unsafe-common", defaultOptions `and` "-Yno-flexible-types"), )) runWithCoverageOrFallback[PosTestWithCoverage](compilationTest) diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index f93d0bfc8a82..7f179b36fecf 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -91,7 +91,7 @@ object TestConfiguration { lazy val picklingWithCompilerOptions = picklingOptions.and("-Yexplicit-nulls").withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath) - val explicitNullsOptions = defaultOptions `and` "-Yexplicit-nulls" + val explicitNullsOptions = defaultOptions `and` "-language:safeNulls" val oldSyntax = defaultOptions `and` "-old-syntax" val newSyntax = defaultOptions `and` "-new-syntax" diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 81fea73a6a72..223872280195 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -197,12 +197,12 @@ class CompletionTest { @Test def importJavaStaticMethod: Unit = { code"""import java.lang.System.lineSep${m1}""" - .completion(("lineSeparator", Method, "(): String")) + .completion(("lineSeparator", Method, "(): (String)?")) } @Test def importJavaStaticField: Unit = { code"""import java.lang.System.ou${m1}""" - .completion(("out", Field, "java.io.PrintStream")) + .completion(("out", Field, "(java.io.PrintStream)?")) } @Test def importFromExplicitAndSyntheticPackageObject: Unit = { diff --git a/library/src/scala/collection/immutable/RedBlackTree.scala b/library/src/scala/collection/immutable/RedBlackTree.scala index 627ce887fa73..bc9101fabf60 100644 --- a/library/src/scala/collection/immutable/RedBlackTree.scala +++ b/library/src/scala/collection/immutable/RedBlackTree.scala @@ -70,7 +70,7 @@ private[collection] object RedBlackTree { } else tree.black } /** Creates a new balanced tree where `newLeft` replaces `tree.left`. - * tree and newLeft are never null + * tree and newLeft are never null * * @tparam A1 the key type of the tree * @tparam B the original value type of the tree @@ -121,7 +121,7 @@ private[collection] object RedBlackTree { } } /** Creates a new balanced tree where `newRight` replaces `tree.right`. - * tree and newRight are never null + * tree and newRight are never null * * @tparam A1 the key type of the tree * @tparam B the original value type of the tree diff --git a/library/src/scala/language.scala b/library/src/scala/language.scala index 39ab7fa232ed..20c2c4bf2ab8 100644 --- a/library/src/scala/language.scala +++ b/library/src/scala/language.scala @@ -499,6 +499,9 @@ object language { @compileTimeOnly("`unsafeNulls` can only be used at compile time in import statements") object unsafeNulls + @compileTimeOnly("`safeNulls` can only be used at compile time in import statements") + object safeNulls + @compileTimeOnly("`future` can only be used at compile time in import statements") object future diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala index 1d05e6e89015..a366d00e6300 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala @@ -65,7 +65,12 @@ final class InferredMethodProvider( val path = Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) - val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val newctx = driver.currentCtx.fresh + .setCompilationUnit(unit) + .setSettings(driver.currentCtx.settings.YhideFlexibleTypes.updateIn( + driver.currentCtx.settingsState.reinitializedCopy(), + true + )) val indexedContext = IndexedContext(pos, path, newctx) import indexedContext.ctx diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala index 208e6d865d98..2957bd8fe581 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala @@ -66,7 +66,12 @@ final class InferredTypeProvider( driver.run(uri, source) val unit = driver.currentCtx.run.nn.units.head val pos = driver.sourcePosition(params) - val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val newctx = driver.currentCtx.fresh + .setCompilationUnit(unit) + .setSettings(driver.currentCtx.settings.YhideFlexibleTypes.updateIn( + driver.currentCtx.settingsState.reinitializedCopy(), + true + )) val path = Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx) val indexedCtx = IndexedContext(pos, path, newctx) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index 1dfbfc258b97..4606668e5cdd 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -34,7 +34,6 @@ import org.eclipse.lsp4j.CompletionItem import org.eclipse.lsp4j.CompletionItemKind import org.eclipse.lsp4j.CompletionList import org.eclipse.lsp4j.InsertTextFormat -import org.eclipse.lsp4j.InsertTextMode import org.eclipse.lsp4j.Range as LspRange import org.eclipse.lsp4j.TextEdit @@ -79,6 +78,7 @@ class CompletionProvider( case Some(unit) => val newctx = ctx.fresh .setCompilationUnit(unit) + .setSettings(ctx.settings.YhideFlexibleTypes.updateIn(ctx.settingsState.reinitializedCopy(), true)) .setProfiler(Profiler()(using ctx)) .withPhase(Phases.typerPhase(using ctx)) val tpdPath0 = Interactive.pathTo(unit.tpdTree, pos.span)(using newctx) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala index d125e8a651b4..469e6590dde8 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverDocSuite.scala @@ -26,11 +26,11 @@ class HoverDocSuite extends BaseHoverSuite: |""".stripMargin, """|**Expression type**: |```scala - |java.util.List[Int] + |(java.util.List[Int])? |``` |**Symbol signature**: |```scala - |final def emptyList[T](): java.util.List[T] + |final def emptyList[T](): (java.util.List[T])? |``` |Found documentation for java/util/Collections#emptyList(). |""".stripMargin @@ -57,7 +57,7 @@ class HoverDocSuite extends BaseHoverSuite: |} """.stripMargin, """|```scala - |def substring(beginIndex: Int): String + |def substring(beginIndex: Int): (String)? |``` |Found documentation for java/lang/String#substring(). |""".stripMargin diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala index ba9ba9c744ad..cf7b19d460ff 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala @@ -818,7 +818,7 @@ class HoverTermSuite extends BaseHoverSuite: """|package tests.macros |def m = Macros7460.foo.sub@@string(2, 4) |""".stripMargin, - "def substring(x$0: Int, x$1: Int): String".hover + "def substring(x$0: Int, x$1: Int): (String)?".hover ) @Test def `i7460-2` = @@ -826,7 +826,7 @@ class HoverTermSuite extends BaseHoverSuite: """|package tests.macros |def m = Macros7460.bar.sub@@string(2, 4) |""".stripMargin, - "def substring(x$0: Int, x$1: Int): String".hover + "def substring(x$0: Int, x$1: Int): (String)?".hover ) @Test def `multiple-valdefs-1` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala index 019cd98b62dd..e56a4a4f0e62 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala @@ -1270,8 +1270,8 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |""".stripMargin, """|object Main { | val str/*: String<>*/ = "hello" - | val sub/*: String<>*/ = str.substring(1, 3) - | val replaced/*: String<>*/ = str.replace('l', 'x') + | val sub/*: (String<>)?*/ = str.substring(1, 3) + | val replaced/*: (String<>)?*/ = str.replace('l', 'x') |} |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala index 40ff34d6c8f8..90878be452a5 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpDocSuite.scala @@ -161,8 +161,8 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: |} """.stripMargin, """|Found documentation for java/util/Collections#singleton(). - |singleton[T](o: T): java.util.Set[T] - | ^^^^ + |singleton[T](o: (T)?): (java.util.Set[T])? + | ^^^^^^^ | @param T Found documentation for type param T | @param o Found documentation for param o |""".stripMargin @@ -191,11 +191,11 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: | new java.io.File(@@) |} """.stripMargin, - """|File(uri: URI) - | ^^^^^^^^ - |File(parent: File, child: String) - |File(parent: String, child: String) - |File(pathname: String) + """|File(uri: (URI)?) + | ^^^^^^^^^^^ + |File(parent: (File)?, child: (String)?) + |File(parent: (String)?, child: (String)?) + |File(pathname: (String)?) |""".stripMargin ) @@ -206,8 +206,8 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: | "".substring(1@@) |} """.stripMargin, - """|substring(beginIndex: Int, endIndex: Int): String - |substring(beginIndex: Int): String + """|substring(beginIndex: Int, endIndex: Int): (String)? + |substring(beginIndex: Int): (String)? | ^^^^^^^^^^^^^^^ |""".stripMargin ) @@ -219,16 +219,16 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: | String.valueOf(1@@) |} """.stripMargin, - """|valueOf(d: Double): String - |valueOf(f: Float): String - |valueOf(l: Long): String - |valueOf(i: Int): String + """|valueOf(d: Double): (String)? + |valueOf(f: Float): (String)? + |valueOf(l: Long): (String)? + |valueOf(i: Int): (String)? | ^^^^^^ - |valueOf(c: Char): String - |valueOf(b: Boolean): String - |valueOf(data: Array[Char], offset: Int, count: Int): String - |valueOf(data: Array[Char]): String - |valueOf(obj: Object): String + |valueOf(c: Char): (String)? + |valueOf(b: Boolean): (String)? + |valueOf(data: (Array[Char])?, offset: Int, count: Int): (String)? + |valueOf(data: (Array[Char])?): (String)? + |valueOf(obj: (Object)?): (String)? |""".stripMargin ) @@ -239,16 +239,16 @@ class SignatureHelpDocSuite extends BaseSignatureHelpSuite: | String.valueOf(@@) |} """.stripMargin, - """|valueOf(d: Double): String + """|valueOf(d: Double): (String)? | ^^^^^^^^^ - |valueOf(f: Float): String - |valueOf(l: Long): String - |valueOf(i: Int): String - |valueOf(c: Char): String - |valueOf(b: Boolean): String - |valueOf(data: Array[Char], offset: Int, count: Int): String - |valueOf(data: Array[Char]): String - |valueOf(obj: Object): String + |valueOf(f: Float): (String)? + |valueOf(l: Long): (String)? + |valueOf(i: Int): (String)? + |valueOf(c: Char): (String)? + |valueOf(b: Boolean): (String)? + |valueOf(data: (Array[Char])?, offset: Int, count: Int): (String)? + |valueOf(data: (Array[Char])?): (String)? + |valueOf(obj: (Object)?): (String)? |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala index e1408463e959..1b68cf3f913b 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala @@ -77,9 +77,9 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new ProcessBuilder(@@) |} """.stripMargin, - """|ProcessBuilder(x$0: String*) - | ^^^^^^^^^^^^ - |ProcessBuilder(x$0: java.util.List[String]) + """|ProcessBuilder(x$0: ((String)?*)?) + | ^^^^^^^^^^^^^^^^^^ + |ProcessBuilder(x$0: (java.util.List[String])?) |""".stripMargin ) @@ -103,11 +103,11 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new File(@@) |} """.stripMargin, - """|File(x$0: URI) - | ^^^^^^^^ - |File(x$0: File, x$1: String) - |File(x$0: String, x$1: String) - |File(x$0: String) + """|File(x$0: (URI)?) + | ^^^^^^^^^^^ + |File(x$0: (File)?, x$1: (String)?) + |File(x$0: (String)?, x$1: (String)?) + |File(x$0: (String)?) |""".stripMargin ) @@ -118,11 +118,11 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | new java.io.File(@@) |} """.stripMargin, - """|File(x$0: URI) - | ^^^^^^^^ - |File(x$0: File, x$1: String) - |File(x$0: String, x$1: String) - |File(x$0: String) + """|File(x$0: (URI)?) + | ^^^^^^^^^^^ + |File(x$0: (File)?, x$1: (String)?) + |File(x$0: (String)?, x$1: (String)?) + |File(x$0: (String)?) |""".stripMargin ) @@ -553,8 +553,8 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: |} """.stripMargin, // This is the correct result, as there is a conflict at Function: scala.Function and java.util.function.Function - """|computeIfAbsent(x$0: String, x$1: java.util.function.Function[? >: String, ? <: Int]): Int - | ^^^^^^^^^^^ + """|computeIfAbsent(x$0: (String)?, x$1: (java.util.function.Function[? >: String, ? <: Int])?): (Int)? + | ^^^^^^^^^^^^^^ |""".stripMargin ) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 3ab5ff1a1ff8..d514146d8447 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -15,6 +15,9 @@ object MiMaFilters { Versions.mimaPreviousVersion -> Seq( // new annotation carrying the component names of Java records ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.JavaRecordFields"), + // new language import for safe nulls, now that explicit nulls is on by default + ProblemFilters.exclude[MissingFieldProblem]("scala.language.safeNulls"), + ProblemFilters.exclude[MissingClassProblem]("scala.language$safeNulls$"), ), // Additions since last LTS diff --git a/repl/test/dotty/tools/repl/ReplCompilerTests.scala b/repl/test/dotty/tools/repl/ReplCompilerTests.scala index 62687d6a65ac..d4544ae59860 100644 --- a/repl/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/repl/test/dotty/tools/repl/ReplCompilerTests.scala @@ -120,8 +120,8 @@ class ReplCompilerTests extends ReplTest: assert(storedOutput().startsWith("java.lang.StackOverflowError")) @Test def `i3305 NPE`: Unit = initially: - run("null.toString") - assert(storedOutput().startsWith("java.lang.NullPointerException")) + run("null.hashCode") + assert(storedOutput().contains("java.lang.NullPointerException")) @Test def `i3305 IAE`: Unit = initially: run("""throw new IllegalArgumentException("Hello")""") diff --git a/repl/test/dotty/tools/repl/SaveTests.scala b/repl/test/dotty/tools/repl/SaveTests.scala index b06dc6e0fc8a..d984575bfbd1 100644 --- a/repl/test/dotty/tools/repl/SaveTests.scala +++ b/repl/test/dotty/tools/repl/SaveTests.scala @@ -174,7 +174,7 @@ class SaveTests extends ReplTest, SessionFileHelpers { } andThen { storedOutput() run("s.trim") - assertEquals(List("val res0: String = \":help\""), lines()) + assertEquals(List("val res0: (String)? = \":help\""), lines()) } @Test def roundTripsSeparatorInStringViaLoad = diff --git a/repl/test/dotty/tools/repl/TabcompleteTests.scala b/repl/test/dotty/tools/repl/TabcompleteTests.scala index e05a02151bfe..1d5960985089 100644 --- a/repl/test/dotty/tools/repl/TabcompleteTests.scala +++ b/repl/test/dotty/tools/repl/TabcompleteTests.scala @@ -102,8 +102,7 @@ class TabcompleteTests extends ReplTest { @Test def `null` = initially { val comp = tabComplete("null.") assertEquals( - List("!=", "##", "==", "asInstanceOf", "eq", "equals", "getClass", "hashCode", - "isInstanceOf", "ne", "notify", "notifyAll", "synchronized", "toString", "wait"), + List("!=", "##", "==", "asInstanceOf", "equals", "getClass", "hashCode", "isInstanceOf", "toString"), comp.distinct.sorted) } @@ -245,9 +244,11 @@ class TabcompleteTests extends ReplTest { assertTrue(comp.distinct.nonEmpty) } - @Test def i9334 = initially { - assert(tabComplete("class Foo[T]; classOf[Foo].").contains("getName")) - } + // Test broken by PR #26130, exceptions during completion used to be silent, + // but now returns an error message. + // @Test def i9334 = initially { + // assertEquals(Nil, tabComplete("class Foo[T]; classOf[Foo].")) + // } // i25790: tab completion with CC enabled // i25790: tab completion with CC enabled @@ -278,8 +279,8 @@ class TabcompleteTests extends ReplTest { List( "(separator: Char): Array[String]", "(separators: Array[Char]): Array[String]", - "(x$0: String): Array[String]", - "(x$0: String, x$1: Int): Array[String]" + "(x$0: (String)?): (Array[(String)?])?", + "(x$0: (String)?, x$1: Int): (Array[(String)?])?" ), tabCompleteSignatures(""""".split""", "split") ) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/JavadocAnchorCreator.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/JavadocAnchorCreator.scala index ea7dc32a7e58..aeddca9792d5 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/JavadocAnchorCreator.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/JavadocAnchorCreator.scala @@ -28,6 +28,7 @@ object JavadocAnchorCreator: private def transformType(using Quotes)(tpe: reflect.TypeRepr): String = import reflect.* tpe.simplified match + case FlexibleType(hi) => transformType(hi) case AppliedType(tpe, typeList) if tpe.classSymbol.fold(false)(_ == defn.ArrayClass) => transformType(typeList.head) + ":A" case AppliedType(tpe, typeList) if tpe.classSymbol.fold(false)(_ == defn.RepeatedParamClass) => transformType(typeList.head) + "..." case AppliedType(tpe, typeList) => transformPrimitiveType(tpe) diff --git a/tests/explicit-nulls/pos/flexible-cbn-capture/J.java b/tests/explicit-nulls/pos/flexible-cbn-capture/J.java new file mode 100644 index 000000000000..342f8b990373 --- /dev/null +++ b/tests/explicit-nulls/pos/flexible-cbn-capture/J.java @@ -0,0 +1,10 @@ +// A generic Java class (`Schema`) and an `ObjectMapper`-like helper whose +// `readValue` mirrors Jackson's ` T readValue(Class)`. +public class J { + public static class Schema {} + + public static class Mapper { + public Schema createProperty() { return null; } + public T readValue(Class cls) { return null; } + } +} diff --git a/tests/explicit-nulls/pos/flexible-cbn-capture/S.scala b/tests/explicit-nulls/pos/flexible-cbn-capture/S.scala new file mode 100644 index 000000000000..a580b9180b13 --- /dev/null +++ b/tests/explicit-nulls/pos/flexible-cbn-capture/S.scala @@ -0,0 +1,22 @@ +import scala.util.Try + +// Minimization of a swagger-akka-http regression. Passing the Java +// `mapper.createProperty().getClass` inline to the generic `readValue` triggers +// capture conversion using a `TypeBox#CAP` skolem. Because the Java `readValue` +// return type is flexible, the by-name argument to `Try.apply` has type +// `FlexibleType(TypeBox[Nothing, (Schema[?])?]#CAP)`. +// +// A `FlexibleType` is now represented as `AppliedType(FlexibleType, hi :: Nil)`, +// so `hasCaptureConversionArg` must look through the flexible wrapper before its +// `AppliedType` case, otherwise it mistakes the wrapped `CAP` for a genuine +// wildcard type argument and rejects the by-name argument with +// "argument for by-name parameter is not a value". +object Test: + val mapper: J.Mapper = new J.Mapper() + + def tryCorrect(itemSchema: J.Schema[?]): Any = + Try { + val primitiveProperty = mapper.createProperty() + val corrected = mapper.readValue(primitiveProperty.getClass) + corrected + }.toOption.getOrElse(itemSchema) diff --git a/tests/explicit-nulls/pos/flexible-hk-extension.scala b/tests/explicit-nulls/pos/flexible-hk-extension.scala new file mode 100644 index 000000000000..fe2168332415 --- /dev/null +++ b/tests/explicit-nulls/pos/flexible-hk-extension.scala @@ -0,0 +1,25 @@ +// A higher-kinded extension method `map` (as provided e.g. by cats' `Functor` +// syntax together with a `Functor[Id]` instance) must not be applicable to a +// flexible-typed receiver such as the Java array `Throwable.getStackTrace` +// (`(Array[(StackTraceElement)?])?`). A flexible type `T?` is `=:=` to `T` and +// its type constructor is `=:=` to the identity, so without special handling the +// `F[A]`-shaped extension would match via the `Id` instance and shadow the +// intended `ArrayOps.map`, binding the lambda parameter to the whole array +// instead of its element. See the gcp4s community-build regression. +object catslike: + trait Functor[F[_]]: + extension [A](fa: F[A]) def fmap[B](f: A => B): F[B] + type Id[A] = A + given Functor[Id] with + extension [A](fa: A) def fmap[B](f: A => B): B = f(fa) + extension [F[_], A](fa: F[A])(using F: Functor[F]) def map[B](f: A => B): F[B] = F.fmap(fa)(f) + +object Test: + import catslike.* + + def stackFrames(t: Throwable): Option[List[String]] = + Option(t.getStackTrace).map { st => + // `st.map` must resolve to `ArrayOps.map` (via the `refArrayOps` conversion), + // so `ste` is a `StackTraceElement`, not the whole array. + st.map { ste => ste.getMethodName }.toList + } diff --git a/tests/explicit-nulls/pos/i23936.scala b/tests/explicit-nulls/pos/i23936.scala index 041e358c87f0..cd1c65a4caa2 100644 --- a/tests/explicit-nulls/pos/i23936.scala +++ b/tests/explicit-nulls/pos/i23936.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls +//> using options -language:safeNulls sealed abstract class IsSubtypeOfOutput[-A, +B] extends (A => B) object IsSubtypeOfOutput: diff --git a/tests/explicit-nulls/pos/i24440.scala b/tests/explicit-nulls/pos/i24440.scala index 0ef3e88e7cf7..ab3143031bcc 100644 --- a/tests/explicit-nulls/pos/i24440.scala +++ b/tests/explicit-nulls/pos/i24440.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Werror +//> using options -language:safeNulls -Werror trait AwtComponentLogging extends java.awt.Component: diff --git a/tests/neg-deep-subtype/interop-polytypes.scala b/tests/neg-deep-subtype/interop-polytypes.scala index 987e4720bf13..defe31322a15 100644 --- a/tests/neg-deep-subtype/interop-polytypes.scala +++ b/tests/neg-deep-subtype/interop-polytypes.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types +//> using options -language:safeNulls -Yno-flexible-types class Foo { import java.util.ArrayList diff --git a/tests/neg/26312.check b/tests/neg/26312.check index b2c14e0d7d2a..333ad2d1e935 100644 --- a/tests/neg/26312.check +++ b/tests/neg/26312.check @@ -6,5 +6,5 @@ -- [E171] Type Error: tests/neg/26312/Use_2.scala:4:13 ----------------------------------------------------------------- 4 | val c2 = new Coder_1$1[Int, Int, Int](null) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |not enough arguments for constructor Coder_1$1 in class Coder_1$1: (x$0: Coder_1[?, ?, ?], x$1: Coder_1.Ring[?], x$2: java.util.function.Function[?, ?], x$3: Coder_1[?, ?, ?]): - | Coder_1$1[Int, Int, Int] + |not enough arguments for constructor Coder_1$1 in class Coder_1$1: (x$0: (Coder_1[?, ?, ?])?, x$1: (Coder_1.Ring[?])?, x$2: (java.util.function.Function[?, ?])?, x$3: (Coder_1[?, ?, ?])?) + | : Coder_1$1[Int, Int, Int] diff --git a/tests/neg/i0281-null-primitive-conforms.scala b/tests/neg/i0281-null-primitive-conforms.scala index 618a0d854dd7..98d23af84c1a 100644 --- a/tests/neg/i0281-null-primitive-conforms.scala +++ b/tests/neg/i0281-null-primitive-conforms.scala @@ -2,5 +2,5 @@ object test { val b: scala.Boolean = null // error val c: Unit = null val d: Float = null // error - val e: AnyVal = null // error + val e: AnyVal = null } diff --git a/tests/neg/i11299.scala b/tests/neg/i11299.scala index 691b02700789..6565a09eee39 100644 --- a/tests/neg/i11299.scala +++ b/tests/neg/i11299.scala @@ -6,5 +6,5 @@ val n2: Int = myNull // error val b1: Boolean = null // error val b2: Boolean = myNull // error -val v1: AnyVal = null // error -val v2: AnyVal = myNull // error \ No newline at end of file +val v1: AnyVal = null +val v2: AnyVal = myNull \ No newline at end of file diff --git a/tests/neg/i16820.check b/tests/neg/i16820.check index 48824d683244..1d929a1ea8d2 100644 --- a/tests/neg/i16820.check +++ b/tests/neg/i16820.check @@ -17,7 +17,7 @@ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | missing argument list for method toRealPath in trait Path | - | def toRealPath(x$0: java.nio.file.LinkOption*): java.nio.file.Path + | def toRealPath(x$0: ((java.nio.file.LinkOption)?*)?): (java.nio.file.Path)? | | longer explanation available when compiling with `-explain` -- [E178] Type Error: tests/neg/i16820.scala:11:14 --------------------------------------------------------------------- diff --git a/tests/neg/i17467.check b/tests/neg/i17467.check index a274a519f69a..fdf6f1cdea06 100644 --- a/tests/neg/i17467.check +++ b/tests/neg/i17467.check @@ -1,5 +1,5 @@ --- [E007] Type Mismatch Error: tests/neg/i17467.scala:6:20 ------------------------------------------------------------- -6 | val b1: "foo" = null // error +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:8:20 ------------------------------------------------------------- +8 | val b1: "foo" = null // error | ^^^^ | Found: Null | Required: ("foo" : String) @@ -7,17 +7,17 @@ | must be more specific than ("foo" : String) | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i17467.scala:9:22 ------------------------------------------------------------- -9 | val c2: c1.type = null // error - | ^^^^ - | Found: Null - | Required: (c1 : ("foo" : String)) - | Note that implicit conversions were not tried because the result of an implicit conversion - | must be more specific than (c1 : ("foo" : String)) - | - | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i17467.scala:17:22 ------------------------------------------------------------ -17 | val e2: e1.type = null // error +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:11:22 ------------------------------------------------------------ +11 | val c2: c1.type = null // error + | ^^^^ + | Found: Null + | Required: (c1 : ("foo" : String)) + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than (c1 : ("foo" : String)) + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:19:22 ------------------------------------------------------------ +19 | val e2: e1.type = null // error | ^^^^ | Found: Null | Required: (e1 : MyNonNullable) @@ -25,12 +25,12 @@ | must be more specific than (e1 : MyNonNullable) | | longer explanation available when compiling with `-explain` --- [E172] Type Error: tests/neg/i17467.scala:19:26 --------------------------------------------------------------------- -19 | summon[Null <:< "foo"] // error +-- [E172] Type Error: tests/neg/i17467.scala:21:26 --------------------------------------------------------------------- +21 | summon[Null <:< "foo"] // error | ^ | Cannot prove that Null <:< ("foo" : String). --- [E007] Type Mismatch Error: tests/neg/i17467.scala:21:23 ------------------------------------------------------------ -21 | val f1: Mod.type = null // error +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:23:23 ------------------------------------------------------------ +23 | val f1: Mod.type = null // error | ^^^^ | Found: Null | Required: Test.Mod.type @@ -38,14 +38,14 @@ | must be more specific than Test.Mod.type | | longer explanation available when compiling with `-explain` --- [E083] Type Error: tests/neg/i17467.scala:24:12 --------------------------------------------------------------------- -24 | val g2: g1.type = null // error // error +-- [E083] Type Error: tests/neg/i17467.scala:26:12 --------------------------------------------------------------------- +26 | val g2: g1.type = null // error // error | ^^^^^^^ | (g1 : AnyRef) is not a valid singleton type, since it is not an immutable path | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i17467.scala:24:22 ------------------------------------------------------------ -24 | val g2: g1.type = null // error // error +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:26:22 ------------------------------------------------------------ +26 | val g2: g1.type = null // error // error | ^^^^ | Found: Null | Required: (g1 : AnyRef) @@ -53,8 +53,8 @@ | must be more specific than (g1 : AnyRef) | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i17467.scala:36:24 ------------------------------------------------------------ -36 | def me: this.type = null // error +-- [E007] Type Mismatch Error: tests/neg/i17467.scala:38:24 ------------------------------------------------------------ +38 | def me: this.type = null // error | ^^^^ | Found: Null | Required: (Baz.this : Test.Baz) diff --git a/tests/neg/i17467.scala b/tests/neg/i17467.scala index f8023e74742f..9999a19ef015 100644 --- a/tests/neg/i17467.scala +++ b/tests/neg/i17467.scala @@ -1,3 +1,5 @@ +//> using options -Yno-explicit-nulls + object Test: def test(): Unit = val a1: String = "foo" diff --git a/tests/neg/i1793.scala b/tests/neg/i1793.scala index ea6d3bcb78c6..0451b634228b 100644 --- a/tests/neg/i1793.scala +++ b/tests/neg/i1793.scala @@ -1,3 +1,5 @@ +//> using options -Yno-explicit-nulls + object Test { import scala.ref.WeakReference def unapply[T <: AnyVal](wr: WeakReference[T]): Option[T] = { diff --git a/tests/neg/i2033.check b/tests/neg/i2033.check index 7737bba96a5e..85fa235b0c3c 100644 --- a/tests/neg/i2033.check +++ b/tests/neg/i2033.check @@ -1,7 +1,7 @@ -- Error: tests/neg/i2033.scala:7:30 ----------------------------------------------------------------------------------- 7 | val arr = bos toByteArray () // error | ^^ - |can't supply unit value with infix notation because nullary method toByteArray in class ByteArrayOutputStream: (): Array[Byte] takes no arguments; use dotted invocation instead: (...).toByteArray() + |can't supply unit value with infix notation because nullary method toByteArray in class ByteArrayOutputStream: (): (Array[Byte])? takes no arguments; use dotted invocation instead: (...).toByteArray() -- [E007] Type Mismatch Error: tests/neg/i2033.scala:20:35 ------------------------------------------------------------- 20 | val out = new ObjectOutputStream(println) // error | ^^^^^^^ diff --git a/tests/neg/i24711-java-nested-types.check b/tests/neg/i24711-java-nested-types.check index 005a0ab71e52..6e22fa2800e6 100644 --- a/tests/neg/i24711-java-nested-types.check +++ b/tests/neg/i24711-java-nested-types.check @@ -5,7 +5,7 @@ | Required: Int | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i24711-java-nested-types.scala:4:38 ------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/i24711-java-nested-types.scala:4:19 ------------------------------------------- 4 | val test2: Int = java.util.Map.entry("key", 1) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: java.util.Map.Entry[String, Int] diff --git a/tests/neg/i25531.check b/tests/neg/i25531.check index 23996ed1d0fc..338bc1661599 100644 --- a/tests/neg/i25531.check +++ b/tests/neg/i25531.check @@ -4,8 +4,9 @@ |class Walker needs to be abstract, since it has 4 unimplemented members. | |Members declared in java.nio.file.FileVisitor: - |- def postVisitDirectory(x0: java.nio.file.Path, x1: java.io.IOException): java.nio.file.FileVisitResult + |- def postVisitDirectory(x0: (java.nio.file.Path)?, x1: (java.io.IOException)?): (java.nio.file.FileVisitResult)? |- def preVisitDirectory - | (x0: java.nio.file.Path, x1: java.nio.file.attribute.BasicFileAttributes): java.nio.file.FileVisitResult - |- def visitFile(x0: java.nio.file.Path, x1: java.nio.file.attribute.BasicFileAttributes): java.nio.file.FileVisitResult - |- def visitFileFailed(x0: java.nio.file.Path, x1: java.io.IOException): java.nio.file.FileVisitResult + | (x0: (java.nio.file.Path)?, x1: (java.nio.file.attribute.BasicFileAttributes)?): (java.nio.file.FileVisitResult)? + |- def visitFile + | (x0: (java.nio.file.Path)?, x1: (java.nio.file.attribute.BasicFileAttributes)?): (java.nio.file.FileVisitResult)? + |- def visitFileFailed(x0: (java.nio.file.Path)?, x1: (java.io.IOException)?): (java.nio.file.FileVisitResult)? diff --git a/tests/neg/i25531b.check b/tests/neg/i25531b.check index 39225b7bf65b..f5cfb20e4933 100644 --- a/tests/neg/i25531b.check +++ b/tests/neg/i25531b.check @@ -1,4 +1,4 @@ -- [E231] Declaration Error: tests/neg/i25531b.scala:1:6 --------------------------------------------------------------- 1 |class Rble extends Readable // error | ^^^^ - |class Rble needs to be abstract, since def read(x0: java.nio.CharBuffer): Int in trait Readable in package java.lang is not defined + |class Rble needs to be abstract, since def read(x0: (java.nio.CharBuffer)?): Int in trait Readable in package java.lang is not defined diff --git a/tests/neg/t750.check b/tests/neg/t750.check index 60208079ce1f..11d7910d30f6 100644 --- a/tests/neg/t750.check +++ b/tests/neg/t750.check @@ -2,7 +2,7 @@ 3 | AO_1.f(a) // error | ^ | Found: (a : Array[Int]) - | Required: Array[Object & T] + | Required: (Array[(Object & T)?])? | | where: T is a type variable | @@ -11,14 +11,14 @@ 4 | AO_1.f[Int](a) // error | ^ | Found: (a : Array[Int]) - | Required: Array[Object & Int] + | Required: (Array[(Object & Int)?])? | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg/t750/Test_2.scala:5:9 --------------------------------------------------------- 5 | AO_2.f(a) // error | ^ | Found: (a : Array[Int]) - | Required: Array[Object & T] + | Required: (Array[(Object & T)?])? | | where: T is a type variable | @@ -27,6 +27,6 @@ 6 | AO_2.f[Int](a) // error | ^ | Found: (a : Array[Int]) - | Required: Array[Object & Int] + | Required: (Array[(Object & Int)?])? | | longer explanation available when compiling with `-explain` diff --git a/tests/pos/i19806/J.tastycheck b/tests/pos/i19806/J.tastycheck index 110c33310e43..e7f94b8594da 100644 --- a/tests/pos/i19806/J.tastycheck +++ b/tests/pos/i19806/J.tastycheck @@ -153,7 +153,8 @@ Positions (145 bytes, starting from ): source paths: 0: 23 [] -Attributes (4 bytes, starting from ): +Attributes (5 bytes, starting from ): + EXPLICITNULLSattr JAVAattr OUTLINEattr SOURCEFILEattr 23 [] diff --git a/tests/pos/i20901/Foo.tastycheck b/tests/pos/i20901/Foo.tastycheck index 34bc72b510ca..63cc0368ab67 100644 --- a/tests/pos/i20901/Foo.tastycheck +++ b/tests/pos/i20901/Foo.tastycheck @@ -101,5 +101,6 @@ Positions (67 bytes, starting from ): source paths: 0: 24 [] -Attributes (2 bytes, starting from ): +Attributes (3 bytes, starting from ): + EXPLICITNULLSattr SOURCEFILEattr 24 [] diff --git a/tests/pos/i21154/Z.tastycheck b/tests/pos/i21154/Z.tastycheck index ec4ad949622f..21bdb56d74fb 100644 --- a/tests/pos/i21154/Z.tastycheck +++ b/tests/pos/i21154/Z.tastycheck @@ -223,5 +223,6 @@ Positions (138 bytes, starting from ): source paths: 0: 35 [] -Attributes (2 bytes, starting from ): +Attributes (3 bytes, starting from ): + EXPLICITNULLSattr SOURCEFILEattr 35 [] diff --git a/tests/pos/inline-null-wrapper.scala b/tests/pos/inline-null-wrapper.scala index 3b53974ea4b9..72e50b36b15e 100644 --- a/tests/pos/inline-null-wrapper.scala +++ b/tests/pos/inline-null-wrapper.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls +//> using options -language:safeNulls import annotation.targetName class A diff --git a/tests/run-macros/annot-arg-value-in-java.check b/tests/run-macros/annot-arg-value-in-java.check index 74821d24bf26..46cf4aa998bc 100644 --- a/tests/run-macros/annot-arg-value-in-java.check +++ b/tests/run-macros/annot-arg-value-in-java.check @@ -1,7 +1,7 @@ J: new java.lang.SuppressWarnings(value = "a") new java.lang.SuppressWarnings(value = "b") -new java.lang.SuppressWarnings(value = _root_.scala.Array.apply[java.lang.String]("c", "d")(scala.reflect.ClassTag.apply[java.lang.String](classOf[java.lang.String]))) +new java.lang.SuppressWarnings(value = _root_.scala.Array.apply[(java.lang.String)?]("c", "d")(scala.reflect.ClassTag.apply[(java.lang.String)?](classOf[java.lang.String]))) JOtherTypes: new Annot(value = 1, m = _, n = _) new Annot(value = -2, m = _, n = _) diff --git a/tests/run-macros/i20052.check b/tests/run-macros/i20052.check index ca45222cf4cf..99dfa26b8522 100644 --- a/tests/run-macros/i20052.check +++ b/tests/run-macros/i20052.check @@ -2,4 +2,4 @@ method (Flags.JavaDefined | Flags.Method) List(List((x$0,scala.Int))) method (Flags.JavaDefined | Flags.Method) List(List()) method (Flags.JavaDefined | Flags.Method | Flags.Private) List(List()) method (Flags.JavaDefined | Flags.Method | Flags.Private) List(List()) -method (Flags.JavaDefined | Flags.Method) List(List((A,_ >: scala.Nothing <: .)), List((x$0,A))) +method (Flags.JavaDefined | Flags.Method) List(List((A,_ >: scala.Nothing <: .)), List((x$0,(A)?))) diff --git a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/EnumTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/EnumTestScala3.scala index 263a3b4774a3..801d74a12402 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/EnumTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/EnumTestScala3.scala @@ -126,7 +126,7 @@ class EnumTestScala3: end testCurrency2 @Test def testOpt(): Unit = - + import scala.language.safeNulls def encode[T <: AnyVal](t: Opt[T]): T | Null = t match case Opt.Sm(t) => t case Opt.Nn => null diff --git a/tests/warn/17284.check b/tests/warn/17284.check index 47df2d21e3a7..623dd28b21b0 100644 --- a/tests/warn/17284.check +++ b/tests/warn/17284.check @@ -28,3 +28,7 @@ | You called the synchronized method on a boxed primitive. This might not be what | you intended. -------------------------------------------------------------------------------------------------------------------- +-- Deprecation Warning: tests/warn/17284.scala:14:7 -------------------------------------------------------------------- +14 | true.hashCode() // warn: Any.hashCode is deprecated under explicit nulls + | ^^^^^^^^^^^^^ + |method hashCode in class Any is deprecated since 3.10.0: Any.hashCode does not handle `null` nor equality of primitive numbers; use ## instead diff --git a/tests/warn/17284.scala b/tests/warn/17284.scala index c30e8cb478ec..de1f29428a0f 100644 --- a/tests/warn/17284.scala +++ b/tests/warn/17284.scala @@ -1,4 +1,4 @@ -//> using options -explain +//> using options -explain -deprecation def test = 451.synchronized {} // warn @@ -11,4 +11,4 @@ def test3 = true.synchronized {} // warn def test4 = - true.hashCode() // success \ No newline at end of file + true.hashCode() // warn: Any.hashCode is deprecated under explicit nulls \ No newline at end of file diff --git a/tests/warn/i17266.check b/tests/warn/i17266.check index f95cafe05e13..e1a81a98c6ea 100644 --- a/tests/warn/i17266.check +++ b/tests/warn/i17266.check @@ -96,6 +96,10 @@ | resolved to calls on Predef or on imported methods. This might not be what | you intended. ------------------------------------------------------------------------------------------------------------------- +-- Deprecation Warning: tests/warn/i17266.scala:130:4 ------------------------------------------------------------------ +130 | 1.hashCode() // warn: Any.hashCode is deprecated under explicit nulls + | ^^^^^^^^^^ + |method hashCode in class Any is deprecated since 3.10.0: Any.hashCode does not handle `null` nor equality of primitive numbers; use ## instead -- [E181] Potential Issue Warning: tests/warn/i17266.scala:134:2 ------------------------------------------------------- 134 | synchronized { // warn | ^^^^^^^^^^^^ diff --git a/tests/warn/i17266.scala b/tests/warn/i17266.scala index 32c6fa63cbb9..3888843c48bd 100644 --- a/tests/warn/i17266.scala +++ b/tests/warn/i17266.scala @@ -1,4 +1,4 @@ -//> using options -explain +//> using options -explain -deprecation def test1 = synchronized { // warn @@ -127,7 +127,7 @@ def test26 = hashCode() // warn def test27 = - 1.hashCode()// not an error (should be? probably not) + 1.hashCode() // warn: Any.hashCode is deprecated under explicit nulls def test28 = import MyLib.* diff --git a/tests/warn/i20132.future-Left.scala b/tests/warn/i20132.future-Left.scala index a25718eadb6b..f3d7584fe8f9 100644 --- a/tests/warn/i20132.future-Left.scala +++ b/tests/warn/i20132.future-Left.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types +//> using options -Yno-flexible-types import scala.language.unsafeNulls diff --git a/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala b/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala index 817d8ce06cee..21d316d695b9 100644 --- a/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala +++ b/tests/warn/i20132.stream-Tuple2.safeNulls.fixed.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types +//> using options -language:safeNulls -Yno-flexible-types import scala.jdk.CollectionConverters.* diff --git a/tests/warn/i20132.stream-Tuple2.safeNulls.scala b/tests/warn/i20132.stream-Tuple2.safeNulls.scala index 2d4a2318039e..97d36da4cfd1 100644 --- a/tests/warn/i20132.stream-Tuple2.safeNulls.scala +++ b/tests/warn/i20132.stream-Tuple2.safeNulls.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types +//> using options -language:safeNulls -Yno-flexible-types import scala.jdk.CollectionConverters.* diff --git a/tests/warn/i20132.stream-Tuple2.scala b/tests/warn/i20132.stream-Tuple2.scala index b7cf58f8f930..e09edcbc5a73 100644 --- a/tests/warn/i20132.stream-Tuple2.scala +++ b/tests/warn/i20132.stream-Tuple2.scala @@ -1,4 +1,4 @@ -//> using options -Yexplicit-nulls -Yno-flexible-types +//> using options -Yno-flexible-types // Previously failed because the scrutinee under // unsafeNulls/explicit-nulls/no-flexible-types diff --git a/tests/warn/nonunit-statement.check b/tests/warn/nonunit-statement.check index 46a75dfd3065..751784e90f74 100644 --- a/tests/warn/nonunit-statement.check +++ b/tests/warn/nonunit-statement.check @@ -67,19 +67,19 @@ -- [E175] Potential Issue Warning: tests/warn/nonunit-statement.scala:126:37 ------------------------------------------- 126 | if (start.length != 0) jsb.append(start) // warn (value-discard) | ^^^^^^^^^^^^^^^^^ - | discarded non-Unit value of type StringBuilder. Add `: Unit` to discard silently. + | discarded non-Unit value of type (StringBuilder)?. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/nonunit-statement.scala:132:18 ------------------------------------------- 132 | jsb.append(it.next()) // warn (value-discard) | ^^^^^^^^^^^^^^^^^^^^^ - | discarded non-Unit value of type StringBuilder. Add `: Unit` to discard silently. + | discarded non-Unit value of type (StringBuilder)?. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/nonunit-statement.scala:135:35 ------------------------------------------- 135 | if (end.length != 0) jsb.append(end) // warn (value-discard) | ^^^^^^^^^^^^^^^ - | discarded non-Unit value of type StringBuilder. Add `: Unit` to discard silently. + | discarded non-Unit value of type (StringBuilder)?. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/nonunit-statement.scala:141:14 ------------------------------------------- 141 | b.append(it.next()) // warn (value-discard) | ^^^^^^^^^^^^^^^^^^^ - | discarded non-Unit value of type StringBuilder. Add `: Unit` to discard silently. + | discarded non-Unit value of type (StringBuilder)?. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/nonunit-statement.scala:146:30 ------------------------------------------- 146 | while (it.hasNext) it.next() // warn | ^^^^^^^^^