diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index 66c247167ed8..ebfeeebe6eb1 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -155,11 +155,17 @@ class DottyPrimitives(ctx: Context) { addPrimitive(defn.Any_asInstanceOf, AS) addPrimitive(defn.Any_##, HASH) + // scala.Reference + addPrimitive(defn.RefEq_eq, ID) + addPrimitive(defn.RefEq_ne, NI) + // java.lang.Object + /* addPrimitive(defn.Object_eq, ID) addPrimitive(defn.Object_ne, NI) - /* addPrimitive(defn.Any_==, EQ) - addPrimitive(defn.Any_!=, NE)*/ + addPrimitive(defn.Any_==, EQ) + addPrimitive(defn.Any_!=, NE) + */ addPrimitive(defn.Object_synchronized, SYNCHRONIZED) /*addPrimitive(defn.Any_isInstanceOf, IS) addPrimitive(defn.Any_asInstanceOf, AS)*/ diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index cbcf3c703f1d..2ef903b4a271 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -43,7 +43,8 @@ class JavaPlatform extends Platform { cls.superClass == defn.ObjectClass && cls.directlyInheritedTraits.forall(_.is(NoInits)) && !ExplicitOuter.needsOuterIfReferenced(cls) && - cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary + cls.typeRef.fields.isEmpty && + !cls.typeRef.abstractTermMembers.exists(_.symbol.isSuperAccessor) /** We could get away with excluding BoxedBooleanClass for the * purpose of equality testing since it need not compare equal diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 74739b612ff9..56ec535aa368 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -39,4 +39,5 @@ object Printers { val typr: Printer = noPrinter val unapp: Printer = noPrinter val variances: Printer = noPrinter + val nullability: Printer = noPrinter } diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index b212e3cbfcfb..3c469ca01331 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -27,8 +27,8 @@ import printing._ import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings} import scala.annotation.internal.sharable - import DenotTransformers.DenotTransformer +import dotty.tools.dotc.core.FlowFacts.NonNullSet import dotty.tools.dotc.profile.Profiler import util.Property.Key import util.Store @@ -142,6 +142,11 @@ object Contexts { protected def gadt_=(gadt: GADTMap): Unit = _gadt = gadt def gadt: GADTMap = _gadt + /** The terms currently known to be non-null (in spite of their declared type) */ + private[this] var _nonNullFacts: NonNullSet = _ + protected def nonNullFacts_=(nnSet: NonNullSet): Unit = _nonNullFacts = nnSet + def nonNullFacts: NonNullSet = _nonNullFacts + /** The history of implicit searches that are currently active */ private[this] var _searchHistory: SearchHistory = null protected def searchHistory_= (searchHistory: SearchHistory): Unit = _searchHistory = searchHistory @@ -487,6 +492,8 @@ object Contexts { def setImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this } def setGadt(gadt: GADTMap): this.type = { this.gadt = gadt; this } def setFreshGADTBounds: this.type = setGadt(gadt.fresh) + def setNonNullFacts(facts: NonNullSet): this.type = { this.nonNullFacts = facts; this } + def addNonNullFacts(facts: NonNullSet): this.type = { setNonNullFacts(this.nonNullFacts ++ facts); this } def setSearchHistory(searchHistory: SearchHistory): this.type = { this.searchHistory = searchHistory; this } def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this } private def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type = { this.moreProperties = moreProperties; this } @@ -563,6 +570,7 @@ object Contexts { typeComparer = new TypeComparer(this) searchHistory = new SearchRoot gadt = EmptyGADTMap + nonNullFacts = FlowFacts.emptyNonNullSet } @sharable object NoContext extends Context { diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 24c7f5910f59..b90b85481a2f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -3,7 +3,7 @@ package dotc package core import Types._, Contexts._, Symbols._, SymDenotations._, StdNames._, Names._ -import Flags._, Scopes._, Decorators._, NameOps._, Periods._ +import Flags._, Scopes._, Decorators._, NameOps._, Periods._, Annotations.Annotation import unpickleScala2.Scala2Unpickler.ensureConstructor import scala.collection.mutable import collection.mutable @@ -278,7 +278,7 @@ class Definitions { lazy val ObjectClass: ClassSymbol = { val cls = ctx.requiredClass("java.lang.Object") assert(!cls.isCompleted, "race for completing java.lang.Object") - cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) + cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: RefEqClass.typeRef :: Nil, newScope) cls.setFlag(NoInits) // The companion object doesn't really exist, `NoType` is the general @@ -295,8 +295,9 @@ class Definitions { lazy val AnyRefAlias: TypeSymbol = enterAliasType(tpnme.AnyRef, ObjectType) def AnyRefType: TypeRef = AnyRefAlias.typeRef - lazy val Object_eq: TermSymbol = enterMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) - lazy val Object_ne: TermSymbol = enterMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) + // TODO(abeln): modify usage sites to use `RefEq_eq/ne`? + lazy val Object_eq: TermSymbol = RefEq_eq + lazy val Object_ne: TermSymbol = RefEq_ne lazy val Object_synchronized: TermSymbol = enterPolyMethod(ObjectClass, nme.synchronized_, 1, pt => MethodType(List(pt.paramRefs(0)), pt.paramRefs(0)), Final) lazy val Object_clone: TermSymbol = enterMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) @@ -327,23 +328,48 @@ class Definitions { pt => MethodType(List(FunctionOf(Nil, pt.paramRefs(0))), pt.paramRefs(0))) /** Method representing a throw */ - lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw, - MethodType(List(ThrowableType), NothingType)) + lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, + MethodType(List(OrType(ThrowableType, NullType)), NothingType)) lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol( ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef)) def NothingType: TypeRef = NothingClass.typeRef lazy val RuntimeNothingModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.Nothing") + + /** `RefEq` is the trait defining the reference equality operators. + * It's just a marker trait and there's no corresponding class file, since it gets erased to `Object`. + */ + lazy val RefEqClass: ClassSymbol = enterCompleteClassSymbol( + ScalaPackageClass, tpnme.RefEq, Trait, AnyClass.typeRef :: Nil) + def RefEqType: TypeRef = RefEqClass.typeRef + + lazy val RefEq_eq: TermSymbol = enterMethod(RefEqClass, nme.eq, MethodType(List(RefEqType), BooleanType), Final) + lazy val RefEq_ne: TermSymbol = enterMethod(RefEqClass, nme.ne, MethodType(List(RefEqType), BooleanType), Final) + + def RefEqMethods: List[TermSymbol] = List(RefEq_eq, RefEq_ne) + lazy val NullClass: ClassSymbol = enterCompleteClassSymbol( - ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef)) + ScalaPackageClass, tpnme.Null, AbstractFinal, AnyClass.typeRef :: RefEqClass.typeRef :: Nil) def NullType: TypeRef = NullClass.typeRef lazy val RuntimeNullModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.Null") + /** An alias for null values that originate in Java code. + * This type gets special treatment in the Typer. Specifically, `JavaNull` can be selected through: + * e.g. + * ``` + * // x: String|Null + * x.length // error: `Null` has no `length` field + * // x2: String|JavaNull + * x2.length // allowed by the Typer, but unsound (might throw NPE) + * ``` + */ + lazy val JavaNull = enterAliasType(tpnme.JavaNull, NullType) + def JavaNullType = JavaNull.typeRef + lazy val ImplicitScrutineeTypeSym = newSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef - lazy val ScalaPredefModuleRef: TermRef = ctx.requiredModuleRef("scala.Predef") def ScalaPredefModule(implicit ctx: Context): Symbol = ScalaPredefModuleRef.symbol @@ -962,10 +988,16 @@ class Definitions { name.length > prefix.length && name.drop(prefix.length).forall(_.isDigit)) - def isBottomClass(cls: Symbol): Boolean = - cls == NothingClass || cls == NullClass - def isBottomType(tp: Type): Boolean = - tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) + def isBottomClass(cls: Symbol) = { + // After erasure, reference types become nullable again. + if (!ctx.phase.erasedTypes) cls == NothingClass + else cls == NothingClass || cls == NullClass + } + def isBottomType(tp: Type) = { + // After erasure, reference types become nullable again. + if (!ctx.phase.erasedTypes) tp.derivesFrom(NothingClass) + else tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) + } /** Is a function class. * - FunctionN for N >= 0 @@ -1054,7 +1086,8 @@ class Definitions { val PredefImportFns: List[() => TermRef] = List[() => TermRef]( () => ScalaPredefModuleRef, - () => DottyPredefModuleRef + () => DottyPredefModuleRef, + () => ctx.requiredModuleRef("scala.NonNull") // TODO(abeln): move to right place ) lazy val RootImportFns: List[() => TermRef] = @@ -1069,7 +1102,7 @@ class Definitions { lazy val UnqualifiedOwnerTypes: Set[NamedType] = RootImportTypes.toSet[NamedType] ++ RootImportTypes.map(_.symbol.moduleClass.typeRef) - lazy val NotRuntimeClasses: Set[Symbol] = Set(AnyClass, AnyValClass, NullClass, NothingClass) + lazy val NotRuntimeClasses: Set[Symbol] = Set(AnyClass, AnyValClass, RefEqClass, NullClass, NothingClass) /** Classes that are known not to have an initializer irrespective of * whether NoInits is set. Note: FunctionXXLClass is in this set @@ -1257,13 +1290,14 @@ class Definitions { def isValueSubClass(sym1: Symbol, sym2: Symbol): Boolean = valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0 - lazy val erasedToObject: Set[Symbol] = Set(AnyClass, AnyValClass, TupleClass, NonEmptyTupleClass, SingletonClass) + lazy val erasedToObject: Set[Symbol] = Set(AnyClass, AnyValClass, RefEqClass, TupleClass, NonEmptyTupleClass, SingletonClass) // ----- Initialization --------------------------------------------------- /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticScalaClasses: List[TypeSymbol] = List( AnyClass, + RefEqClass, AnyRefAlias, AnyKindClass, RepeatedParamClass, @@ -1280,7 +1314,7 @@ class Definitions { /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticCoreMethods: List[TermSymbol] = - AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) + AnyMethods ++ ObjectMethods ++ RefEqMethods ++ List(String_+, throwMethod) lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 26a71b41747b..4b2423fe313e 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -478,7 +478,7 @@ object Flags { final val FromStartFlags: FlagSet = Module | Package | Deferred | Method.toCommonFlags | HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags | - Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic | + Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic | JavaDefined | CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | Extension.toCommonFlags | NonMember | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline diff --git a/compiler/src/dotty/tools/dotc/core/FlowFacts.scala b/compiler/src/dotty/tools/dotc/core/FlowFacts.scala new file mode 100644 index 000000000000..5e28fa9f7ffc --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/FlowFacts.scala @@ -0,0 +1,166 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.ast.tpd._ +import StdNames.nme +import dotty.tools.dotc.ast.Trees.{Tree => _, _} +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Types.{NonNullTermRef, TermRef, Type} + +import scala.annotation.internal.sharable + +/** Operations on flow-sensitive type information */ +object FlowFacts { + + /** A set of `TermRef`s known to be non-null at the current program point */ + type NonNullSet = Set[TermRef] + + /** The initial state where no `TermRef`s are known to be non-null */ + @sharable val emptyNonNullSet = Set.empty[TermRef] + + /** Is `tref` non-null (even if its info says it isn't)? */ + def isNonNull(nnSet: NonNullSet, tref: TermRef): Boolean = { + nnSet.contains(tref) + } + + /** Try to improve the precision of `tpe` using flow-sensitive type information. */ + def refineType(tpe: Type)(implicit ctx: Context): Type = tpe match { + case tref: TermRef if isNonNull(ctx.nonNullFacts, tref) => + NonNullTermRef.fromTermRef(tref) + case _ => tpe + } + + /** Nullability facts inferred from a condition. + * @param ifTrue are the terms known to be non-null if the condition is true. + * @param ifFalse are the terms known to be non-null if the condition is false. + */ + case class Inferred(ifTrue: NonNullSet, ifFalse: NonNullSet) { + // Let `NN(e, true/false)` be the set of terms that are non-null if `e` evaluates to `true/false`. + // We can use De Morgan's laws to underapproximate `NN` via `Inferred`. + // e.g. say `e = e1 && e2`. Then if `e` is `false`, we know that either `!e1` or `!e2`. + // Let `t` be a term that is in both `NN(e1, false)` and `NN(e2, false)`. + // Then it follows that `t` must be in `NN(e, false)`. This means that if we set + // `Inferred(e1 && e2, false) = Inferred(e1, false) ∩ Inferred(e2, false)`, we'll have + // `Inferred(e1 && e2, false) ⊂ NN(e1 && e2, false)` (formally, we'd do a structural induction on `e`). + // This means that when we infer something we do so soundly. The methods below use this approach. + + /** If `this` corresponds to a condition `e1` and `other` to `e2`, calculate the inferred facts for `e1 && e2`. */ + def combineAnd(other: Inferred): Inferred = Inferred(ifTrue.union(other.ifTrue), ifFalse.intersect(other.ifFalse)) + + /** If `this` corresponds to a condition `e1` and `other` to `e2`, calculate the inferred facts for `e1 || e2`. */ + def combineOr(other: Inferred): Inferred = Inferred(ifTrue.intersect(other.ifTrue), ifFalse.union(other.ifFalse)) + + /** The inferred facts for the negation of this condition. */ + def negate: Inferred = Inferred(ifFalse, ifTrue) + } + + object Inferred { + /** Create a singleton inferred fact containing `tref`. */ + def apply(tref: TermRef, ifTrue: Boolean): Inferred = { + if (ifTrue) Inferred(Set(tref), emptyNonNullSet) + else Inferred(emptyNonNullSet, Set(tref)) + } + } + + /** Analyze the tree for a condition `cond` to learn new facts about non-nullability. + * Supports ands, ors, and unary negation. + * + * Example: + * (1) + * ``` + * val x: String|Null = "foo" + * if (x != null) { + * // x: String in the "then" branch + * } + * ``` + * Notice that `x` must be stable for the above to work. + * + * TODO(abeln): add longer description of the algorithm + */ + def inferNonNull(cond: Tree)(implicit ctx: Context): Inferred = { + /** Combine two sets of facts according to `op`. */ + def combine(lhs: Inferred, op: Name, rhs: Inferred): Inferred = { + op match { + case _ if op == nme.ZAND => lhs.combineAnd(rhs) + case _ if op == nme.ZOR => lhs.combineOr(rhs) + } + } + + val emptyFacts = Inferred(emptyNonNullSet, emptyNonNullSet) + val nullLit = tpd.Literal(Constant(null)) + + /** Recurse over a conditional to extract flow facts. */ + def recur(tree: Tree): Inferred = { + tree match { + case Apply(Select(lhs, op), List(rhs)) => + if (op == nme.ZAND || op == nme.ZOR) combine(recur(lhs), op, recur(rhs)) + else if (op == nme.EQ || op == nme.NE || op == nme.eq || op == nme.ne) newFact(lhs, isEq = (op == nme.EQ || op == nme.eq), rhs) + else emptyFacts + case TypeApply(Select(lhs, op), List(targ)) if op == nme.isInstanceOf_ && targ.tpe.isRefToNull => + // TODO(abeln): handle type test with argument that's not a subtype of `Null`. + // We could infer "non-null" in that case: e.g. `if (x.isInstanceOf[String]) { // x can't be null }` + newFact(lhs, isEq = true, nullLit) + case Select(lhs, neg) if neg == nme.UNARY_! => recur(lhs).negate + case Block(_, expr) => recur(expr) + case Inlined(_, _, expansion) => recur(expansion) + case Typed(expr, _) => recur(expr) // TODO(abeln): check that the type is `Boolean`? + case _ => emptyFacts + } + } + + /** Extract new facts from an expression `lhs = rhs` or `lhs != rhs` + * if either the lhs or rhs is the `null` literal. + */ + def newFact(lhs: Tree, isEq: Boolean, rhs: Tree): Inferred = { + def isNullLit(tree: Tree): Boolean = tree match { + case Literal(const) if const.tag == Constants.NullTag => true + case _ => false + } + + def isStableTermRef(tree: Tree): Boolean = asStableTermRef(tree).isDefined + + def asStableTermRef(tree: Tree): Option[TermRef] = tree.tpe match { + case tref: TermRef if tref.isStable => Some(tref) + case _ => None + } + + val trefOpt = + if (isNullLit(lhs) && isStableTermRef(rhs)) asStableTermRef(rhs) + else if (isStableTermRef(lhs) && isNullLit(rhs)) asStableTermRef(lhs) + else None + + trefOpt match { + case Some(tref) => + // If `isEq`, then the condition is of the form `lhs == null`, + // in which case we know `lhs` is non-null if the condition is false. + Inferred(tref, ifTrue = !isEq) + case _ => emptyFacts + } + } + + recur(cond) + } + + /** Propagate flow-sensitive type information inside a condition. + * Specifically, if `cond` is of the form `lhs &&` or `lhs ||`, where the lhs has already been typed + * (and the rhs hasn't been typed yet), compute the non-nullability info we get from lhs and + * return a new context with it. The new context can then be used to type the rhs. + * + * This is useful in e.g. + * ``` + * val x: String|Null = ??? + * if (x != null && x.length > 0) ... + * ``` + */ + def propagateWithinCond(cond: Tree)(implicit ctx: Context): Context = { + cond match { + case Select(lhs, op) if op == nme.ZAND || op == nme.ZOR => + val Inferred(ifTrue, ifFalse) = FlowFacts.inferNonNull(lhs) + if (op == nme.ZAND) ctx.fresh.addNonNullFacts(ifTrue) + else ctx.fresh.addNonNullFacts(ifFalse) + case _ => ctx + } + } +} diff --git a/compiler/src/dotty/tools/dotc/core/JavaNull.scala b/compiler/src/dotty/tools/dotc/core/JavaNull.scala new file mode 100644 index 000000000000..82fb1ba59873 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/JavaNull.scala @@ -0,0 +1,163 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags.JavaDefined +import dotty.tools.dotc.core.StdNames.{jnme, nme} +import dotty.tools.dotc.core.Symbols.{Symbol, defn, _} +import dotty.tools.dotc.core.Types.{AndType, AppliedType, LambdaType, MethodType, OrType, PolyType, Type, TypeAlias, TypeMap, TypeParamRef, TypeRef} + +/** Transformation from Java (nullable) to Scala (non-nullable) types */ +object JavaNull { + + /** Adds nullability annotations to a Java-defined member. + * `tp` is the member type. The type inside `sym` shouldn't be used (might not be even set). + */ + def nullifyMember(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { + assert(sym.is(JavaDefined), s"can only nullify java-defined members") + + // A list of members that are special-cased. + val whitelist: Seq[NullifyPolicy] = Seq( + // The `TYPE` field in every class. + FieldP(_.name == nme.TYPE_), + // The `toString` method. + MethodP(_.name == nme.toString_), + // Constructors: params are nullified, but the result type isn't. + paramsOnlyP(_.isConstructor) + ) ++ Seq( + // Methods in `java.lang.String`. + paramsOnlyP(_.name == nme.concat), + paramsOnlyP(_.name == nme.replace), + paramsOnlyP(_.name == nme.replaceFirst), + paramsOnlyP(_.name == nme.replaceAll), + paramsOnlyP(_.name == nme.split), + paramsOnlyP(_.name == nme.toLowerCase), + paramsOnlyP(_.name == nme.toUpperCase), + paramsOnlyP(_.name == nme.trim), + paramsOnlyP(_.name == nme.toCharArray), + paramsOnlyP(_.name == nme.substring) + ).map(WithinSym(_, defn.StringClass)) ++ Seq( + // Methods in `java.lang.Class` + paramsOnlyP(_.name == nme.newInstance), + paramsOnlyP(_.name == nme.asSubclass), + paramsOnlyP(_.name == jnme.ForName), + ).map(WithinSym(_, defn.ClassClass)) + + + val (fromWhitelistTp, handled) = whitelist.foldLeft((tp, false)) { + case (res@(_, true), _) => res + case ((_, false), pol) => + if (pol.isApplicable(sym)) (pol(tp), true) + else (tp, false) + } + + if (handled) { + fromWhitelistTp + } else { + // Default case: nullify everything. + nullifyType(tp) + } + } + + /** A policy that special cases the handling of some symbol or class of symbols. */ + private sealed trait NullifyPolicy { + /** Whether the policy applies to `sym`. */ + def isApplicable(sym: Symbol): Boolean + /** Nullifies `tp` according to the policy. Should call `isApplicable` first. */ + def apply(tp: Type): Type + } + + /** A policy that avoids modifying a field. */ + private case class FieldP(trigger: Symbol => Boolean)(implicit ctx: Context) extends NullifyPolicy { + override def isApplicable(sym: Symbol): Boolean = trigger(sym) + override def apply(tp: Type): Type = { + assert(!tp.isJavaMethod, s"FieldPolicy applies to method (non-field) type ${tp.show}") + tp + } + } + + /** A policy for handling a method or poly. Can indicate whether the argument or return types should be nullified. */ + private case class MethodP(trigger: Symbol => Boolean, + nlfyParams: Boolean = false, + nlfyRes: Boolean = false) + (implicit ctx: Context) extends TypeMap with NullifyPolicy { + override def isApplicable(sym: Symbol): Boolean = trigger(sym) + + override def apply(tp: Type): Type = { + tp match { + case ptp: PolyType => + derivedLambdaType(ptp)(ptp.paramInfos, this(ptp.resType)) + case mtp: MethodType => + val paramTpes = if (nlfyParams) mtp.paramInfos.mapConserve(nullifyType) else mtp.paramInfos + val resTpe = if (nlfyRes) nullifyType(mtp.resType) else mtp.resType + derivedLambdaType(mtp)(paramTpes, resTpe) + } + } + } + + /** A policy that nullifies only method parameters (but not result types). */ + private def paramsOnlyP(trigger: Symbol => Boolean)(implicit ctx: Context): MethodP = { + MethodP(trigger, nlfyParams = true, nlfyRes = false) + } + + /** A wrapper policy that works as `inner` but additionally verifies that the symbol is contained in `owner`. */ + private case class WithinSym(inner: NullifyPolicy, owner: Symbol)(implicit ctx: Context) extends NullifyPolicy { + override def isApplicable(sym: Symbol): Boolean = sym.owner == owner && inner.isApplicable(sym) + + override def apply(tp: Type): Type = inner(tp) + } + + /** Nullifies a Java type by adding `| JavaNull` in the relevant places. + * We need this because Java types remain implicitly nullable. + */ + private def nullifyType(tpe: Type)(implicit ctx: Context): Type = { + val nullMap = new JavaNullMap(alreadyNullable = false) + nullMap(tpe) + } + + /** A type map that adds `| JavaNull`. + * @param alreadyNullable whether the type being mapped is already nullable (at the outermost level) + */ + class JavaNullMap(alreadyNullable: Boolean)(implicit ctx: Context) extends TypeMap { + /** Should we nullify `tp` at the outermost level? */ + def shouldNullify(tp: Type): Boolean = { + !alreadyNullable && (tp match { + case tp: TypeRef => !tp.symbol.isValueClass && !tp.isRef(defn.AnyClass) && !tp.isRef(defn.RefEqClass) + case _ => true + }) + } + + /** Should we nullify the arguments to the given generic `tp`? + * We only nullify the inside of Scala-defined constructors. + * This is because Java classes are _all_ nullified, so both `java.util.List[String]` and + * `java.util.List[String|Null]` contain nullable elements. + */ + def shouldDescend(tp: AppliedType): Boolean = { + val AppliedType(tycons, _) = tp + tycons.widenDealias match { + case tp: TypeRef if !tp.symbol.is(JavaDefined) => true + case _ => false + } + } + + override def apply(tp: Type): Type = { + tp match { + case tp: LambdaType => mapOver(tp) + case tp: TypeAlias => mapOver(tp) + case tp@AndType(tp1, tp2) => + // nullify(A & B) = (nullify(A) & nullify(B)) | Null, but take care not to add + // duplicate `Null`s at the outermost level inside `A` and `B`. + val newMap = new JavaNullMap(alreadyNullable = true) + derivedAndType(tp, newMap(tp1), newMap(tp2)).toJavaNullable + case tp@OrType(tp1, tp2) if !tp.isJavaNullableUnion => + val newMap = new JavaNullMap(alreadyNullable = true) + derivedOrType(tp, newMap(tp1), newMap(tp2)).toJavaNullable + case tp: TypeRef if shouldNullify(tp) => tp.toJavaNullable + case tp: TypeParamRef if shouldNullify(tp) => tp.toJavaNullable + case appTp@AppliedType(tycons, targs) => + val targs2 = if (shouldDescend(appTp)) targs map this else targs + derivedAppliedType(appTp, tycons, targs2).toJavaNullable + case _ => tp + } + } + } +} diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f618a91271c6..08ef353e0eab 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -160,6 +160,7 @@ object StdNames { scala.List(Byte, Char, Short, Int, Long, Float, Double, Boolean, Unit) // some types whose companions we utilize + final val RefEq: N = "RefEq" final val AnyRef: N = "AnyRef" final val Array: N = "Array" final val List: N = "List" @@ -196,6 +197,7 @@ object StdNames { final val Mirror: N = "Mirror" final val Nothing: N = "Nothing" final val Null: N = "Null" + final val JavaNull: N = "JavaNull" final val Object: N = "Object" final val PartialFunction: N = "PartialFunction" final val PrefixType: N = "PrefixType" @@ -381,6 +383,7 @@ object StdNames { val asClass: N = "asClass" val asInstanceOf_ : N = "asInstanceOf" val assert_ : N = "assert" + val asSubclass: N = "asSubclass" val assume_ : N = "assume" val box: N = "box" val build : N = "build" @@ -391,6 +394,7 @@ object StdNames { val ClassManifestFactory: N = "ClassManifestFactory" val classOf: N = "classOf" val clone_ : N = "clone" + val concat : N = "concat" val conforms_ : N = "$conforms" val copy: N = "copy" val currentMirror: N = "currentMirror" @@ -468,6 +472,7 @@ object StdNames { val name: N = "name" val ne: N = "ne" val newFreeTerm: N = "newFreeTerm" + val newInstance: N = "newInstance" val newFreeType: N = "newFreeType" val newScopeWith: N = "newScopeWith" val next: N = "next" @@ -492,6 +497,9 @@ object StdNames { val reflect : N = "reflect" val reflectiveSelectable: N = "reflectiveSelectable" val reify : N = "reify" + val replace : N = "replace" + val replaceFirst : N = "replaceFirst" + val replaceAll : N = "replaceAll" val rootMirror : N = "rootMirror" val run: N = "run" val runOrElse: N = "runOrElse" @@ -514,10 +522,12 @@ object StdNames { val setType: N = "setType" val setTypeSignature: N = "setTypeSignature" val splice: N = "splice" + val split: N = "split" val staticClass : N = "staticClass" val staticModule : N = "staticModule" val staticPackage : N = "staticPackage" val strictEquality: N = "strictEquality" + val substring: N = "substring" val synchronized_ : N = "synchronized" val tag: N = "tag" val tail: N = "tail" @@ -526,13 +536,17 @@ object StdNames { val thisPrefix : N = "thisPrefix" val throw_ : N = "throw" val toArray: N = "toArray" + val toCharArray: N = "toCharArray" val toList: N = "toList" + val toLowerCase: N = "toLowerCase" val toObjectArray : N = "toObjectArray" val toSeq: N = "toSeq" val toString_ : N = "toString" val toTypeConstructor: N = "toTypeConstructor" + val toUpperCase: N = "toUpperCase" val tpe : N = "tpe" val tree : N = "tree" + val trim: N = "trim" val true_ : N = "true" val typedProductIterator: N = "typedProductIterator" val typeTagToManifest: N = "typeTagToManifest" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 9e08f4c4f821..b7753b854045 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -680,9 +680,17 @@ object SymDenotations { // after Erasure and to avoid cyclic references caused by forcing denotations } - /** Is this symbol a class references to which that are supertypes of null? */ - final def isNullableClass(implicit ctx: Context): Boolean = + /** Is this symbol a class with nullable values? */ + final def isNullableClass(implicit ctx: Context): Boolean = { + if (!ctx.phase.erasedTypes) symbol == defn.NullClass || symbol == defn.AnyClass + else isNullableClassAfterErasure + } + + /** Is this symbol a class with nullable values after erasure? */ + final def isNullableClassAfterErasure(implicit ctx: Context): Boolean = { + // After erasure, reference types become nullable again. isClass && !isValueClass && !is(ModuleClass) && symbol != defn.NothingClass + } /** Is this definition accessible as a member of tree with type `pre`? * @param pre The type of the tree from which the selection is made @@ -1549,7 +1557,7 @@ object SymDenotations { derivesFrom(base) || base.isClass && ( (symbol eq defn.NothingClass) || - (symbol eq defn.NullClass) && (base ne defn.NothingClass)) + (symbol eq defn.NullClass) && base.isNullableClass) final override def typeParamCreationFlags: FlagSet = ClassTypeParamCreationFlags diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index c38b777a3b20..ff0e4516ce9f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -456,10 +456,12 @@ class TypeApplications(val self: Type) extends AnyVal { */ def underlyingIfRepeated(isJava: Boolean)(implicit ctx: Context): Type = if (self.isRepeatedParam) { + val self1 = self.stripJavaNull val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass // If `isJava` is set, then we want to turn `RepeatedParam[T]` into `Array[_ <: T]`, // since arrays aren't covariant until after erasure. See `tests/pos/i5140`. - translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = isJava) + val trans = self1.translateParameterized(defn.RepeatedParamClass, seqClass, wildcardArg = isJava) + if (isJava) trans.toJavaNullable else trans } else self diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 9318c9e063b2..83ee02cfa249 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -248,54 +248,62 @@ object TypeErasure { * The reason to pick last is that we prefer classes over traits that way, * which leads to more predictable bytecode and (?) faster dynamic dispatch. */ - def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { - case JavaArrayType(elem1) => - import dotty.tools.dotc.transform.TypeUtils._ - tp2 match { - case JavaArrayType(elem2) => - if (elem1.isPrimitiveValueType || elem2.isPrimitiveValueType) { - if (elem1.classSymbol eq elem2.classSymbol) // same primitive - JavaArrayType(elem1) - else defn.ObjectType - } else JavaArrayType(erasedLub(elem1, elem2)) - case _ => defn.ObjectType - } - case _ => - tp2 match { - case JavaArrayType(_) => defn.ObjectType - case _ => - val cls2 = tp2.classSymbol - - /** takeWhile+1 */ - def takeUntil[T](l: List[T])(f: T => Boolean): List[T] = { - @tailrec def loop(tail: List[T], acc: List[T]): List[T] = - tail match { - case h :: t => loop(if (f(h)) t else Nil, h :: acc) - case Nil => acc.reverse - } - loop(l, Nil) - } - - // We are not interested in anything that is not a supertype of tp2 - val tp2superclasses = tp1.baseClasses.filter(cls2.derivesFrom) - - // From the spec, "Linearization also satisfies the property that a - // linearization of a class always contains the linearization of its - // direct superclass as a suffix"; it's enough to consider every - // candidate up to the first class. - val candidates = takeUntil(tp2superclasses)(!_.is(Trait)) - - // Candidates st "no other common superclass or trait derives from S" - val minimums = candidates.filter { cand => - candidates.forall(x => !x.derivesFrom(cand) || x.eq(cand)) - } - - // Pick the last minimum to prioritise classes over traits - minimums.lastOption match { - case Some(lub) => valueErasure(lub.typeRef) - case _ => defn.ObjectType - } - } + def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { + // After erasure, C | Null is just C, if C is a reference type. + // We need to short-circuit this case here because the regular lub logic below + // relies on the class hierarchy, which doesn't properly capture `Null`s subtyping + // behaviour. + if (tp1.isRefToNull && tp2.derivesFrom(defn.ObjectClass)) return tp2 + if (tp2.isRefToNull && tp1.derivesFrom(defn.ObjectClass)) return tp1 + tp1 match { + case JavaArrayType(elem1) => + import dotty.tools.dotc.transform.TypeUtils._ + tp2 match { + case JavaArrayType(elem2) => + if (elem1.isPrimitiveValueType || elem2.isPrimitiveValueType) { + if (elem1.classSymbol eq elem2.classSymbol) // same primitive + JavaArrayType(elem1) + else defn.ObjectType + } else JavaArrayType(erasedLub(elem1, elem2)) + case _ => defn.ObjectType + } + case _ => + tp2 match { + case JavaArrayType(_) => defn.ObjectType + case _ => + val cls2 = tp2.classSymbol + + /** takeWhile+1 */ + def takeUntil[T](l: List[T])(f: T => Boolean): List[T] = { + @tailrec def loop(tail: List[T], acc: List[T]): List[T] = + tail match { + case h :: t => loop(if (f(h)) t else Nil, h :: acc) + case Nil => acc.reverse + } + loop(l, Nil) + } + + // We are not interested in anything that is not a supertype of tp2 + val tp2superclasses = tp1.baseClasses.filter(cls2.derivesFrom) + + // From the spec, "Linearization also satisfies the property that a + // linearization of a class always contains the linearization of its + // direct superclass as a suffix"; it's enough to consider every + // candidate up to the first class. + val candidates = takeUntil(tp2superclasses)(!_.is(Trait)) + + // Candidates st "no other common superclass or trait derives from S" + val minimums = candidates.filter { cand => + candidates.forall(x => !x.derivesFrom(cand) || x.eq(cand)) + } + + // Pick the last minimum to prioritise classes over traits + minimums.lastOption match { + case Some(lub) => valueErasure(lub.typeRef) + case _ => defn.ObjectType + } + } + } } /** The erased greatest lower bound of two erased type picks one of the two argument types. diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c52b244d1671..75587413142a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -33,6 +33,7 @@ import scala.util.hashing.{ MurmurHash3 => hashing } import config.Printers.{core, typr} import reporting.trace import java.lang.ref.WeakReference +import dotty.tools.dotc.transform.SymUtils._ import scala.annotation.internal.sharable @@ -236,6 +237,46 @@ object Types { } } + /** Is this type exactly `Null` (no vars, aliases, refinements etc allowed)? */ + def isNullType(implicit ctx: Context): Boolean = this match { + case tp: TypeRef => tp.symbol eq defn.NullClass + case _ => false + } + + /** Is this type a reference to `Null`, possibly after aliasing? */ + def isRefToNull(implicit ctx: Context): Boolean = this.isRef(defn.NullClass) + + /** Is this (after widening and dealiasing) a type of the form `T | Null`? */ + def isNullableUnion(implicit ctx: Context): Boolean = this.widenDealias.normNullableUnion match { + case OrType(_, right) => right.isRefToNull + case _ => false + } + + /** Is this type guaranteed not to have `null` as a value? */ + final def isNotNull(implicit ctx: Context): Boolean = this match { + case tp: ConstantType => tp.value.value != null + case tp: ClassInfo => !tp.cls.isNullableClass && tp.cls != defn.NothingClass + case tp: TypeBounds => tp.lo.isNotNull + case tp: TypeProxy => tp.underlying.isNotNull + case AndType(tp1, tp2) => tp1.isNotNull || tp2.isNotNull + case OrType(tp1, tp2) => tp1.isNotNull && tp2.isNotNull + case _ => false + } + + /** Is this type exactly `JavaNull` (no vars, aliases, refinements etc allowed)? */ + def isJavaNullType(implicit ctx: Context): Boolean = { + // We can't do `this == defn.JavaNull` because when trees are unpickled new references + // to `JavaNull` could be created that are different from `defn.JavaNull`. + // Instead, we compare the symbol. + this.isDirectRef(defn.JavaNull) + } + + /** Is this (after widening and dealiasing) a type of the form `T | JavaNull`? */ + def isJavaNullableUnion(implicit ctx: Context): Boolean = this.widenDealias.normNullableUnion match { + case OrType(_, right) => right.isJavaNullType + case _ => false + } + /** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */ def isBottomType(implicit ctx: Context): Boolean = this match { case tp: TypeRef => tp.symbol eq defn.NothingClass @@ -265,17 +306,6 @@ object Types { loop(this) } - /** Is this type guaranteed not to have `null` as a value? */ - final def isNotNull(implicit ctx: Context): Boolean = this match { - case tp: ConstantType => tp.value.value != null - case tp: ClassInfo => !tp.cls.isNullableClass && tp.cls != defn.NothingClass - case tp: TypeBounds => tp.lo.isNotNull - case tp: TypeProxy => tp.underlying.isNotNull - case AndType(tp1, tp2) => tp1.isNotNull || tp2.isNotNull - case OrType(tp1, tp2) => tp1.isNotNull && tp2.isNotNull - case _ => false - } - /** Is this type produced as a repair for an error? */ final def isError(implicit ctx: Context): Boolean = stripTypeVar.isInstanceOf[ErrorType] @@ -306,8 +336,12 @@ object Types { (new isGroundAccumulator).apply(true, this) /** Is this a type of a repeated parameter? */ - def isRepeatedParam(implicit ctx: Context): Boolean = - typeSymbol eq defn.RepeatedParamClass + def isRepeatedParam(implicit ctx: Context): Boolean = { + def isRep(tpe: Type) = tpe.typeSymbol eq defn.RepeatedParamClass + // A repeated param is represented as either RepeatedParamClass[ElemTpe] or + // RepeatedParamClass[ElemTpe]|JavaNull, if it comes from Java. + isRep(this) || isRep(stripJavaNull) + } /** Is this the type of a method that has a repeated parameter type as * last parameter type? @@ -425,6 +459,13 @@ object Types { val rsym = r.classSymbol if (lsym isSubClass rsym) rsym else if (rsym isSubClass lsym) lsym + else if (this.isNullableUnion) { + val OrType(left, _) = this.normNullableUnion + // If `left` is a reference type, then the class LUB of `left | Null` is `RefEq`. + // This is another one-of case that keeps this method sound, but not complete. + if (left.classSymbol isSubClass defn.ObjectClass) defn.RefEqClass + else NoSymbol + } else NoSymbol case _ => NoSymbol @@ -583,11 +624,19 @@ object Types { case AndType(l, r) => goAnd(l, r) case tp: OrType => - // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix` - // achieved that by narrowing `pre` to each alternative, but it led to merge errors in - // lots of places. The present strategy is instead of widen `tp` using `join` to be a - // supertype of `pre`. - go(tp.join) + if (tp.isJavaNullableUnion) { + // Selecting `name` from a type `T|JavaNull` is like selecting name` from `T`. + // This can throw at runtime, but we trade soundness for usability. + // We need to strip JavaNull from both the type and the prefix so that + // `pre <: tp` continues to hold. + tp.stripJavaNull.findMember(name, pre.stripJavaNull, required, excluded) + } else { + // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix` + // achieved that by narrowing `pre` to each alternative, but it led to merge errors in + // lots of places. The present strategy is instead of widen `tp` using `join` to be a + // supertype of `pre`. + go(tp.join) + } case tp: JavaArrayType => defn.ObjectType.findMember(name, pre, required, excluded) case err: ErrorType => @@ -964,6 +1013,27 @@ object Types { case _ => this } + /** Strips the nullability from a type: `T | Null` goes to `T` */ + def stripNull(implicit ctx: Context): Type = this.widenDealias.normNullableUnion match { + case OrType(left, right) if right.isRefToNull => left.stripNull + case _ => this + } + + /** Strips the Java nullability from a type: `T | JavaNull` goes to `T` */ + def stripJavaNull(implicit ctx: Context): Type = this.widenDealias.normNullableUnion match { + case OrType(left, right) if right.isJavaNullType => left.stripJavaNull + case _ => this + } + + /** Normalizes nullable unions so that `Null` is the last operand (e.g. `Null|T` goes to `T|Null`) */ + def normNullableUnion(implicit ctx: Context): Type = { + // TODO(abeln): make normalization more robust (e.g. so it can handle nested unions). + this match { + case OrType(left, right) if left.isRefToNull => OrType(right, left) + case _ => this + } + } + /** Widen from singleton type to its underlying non-singleton * base type by applying one or more `underlying` dereferences, * Also go from => T to T. @@ -1030,9 +1100,14 @@ object Types { */ def widenUnion(implicit ctx: Context): Type = this match { case OrType(tp1, tp2) => - ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match { - case union: OrType => union.join - case res => res + if (isNullableUnion) { + val OrType(leftTpe, nullTpe) = normNullableUnion + OrType(leftTpe.widenUnion, nullTpe) + } else { + ctx.typeComparer.lub(tp1.widenUnion, tp2.widenUnion, canConstrain = true) match { + case union: OrType => union.join + case res => res + } } case tp @ AndType(tp1, tp2) => tp derived_& (tp1.widenUnion, tp2.widenUnion) @@ -1193,7 +1268,7 @@ object Types { /** If this is a repeated type, its element type, otherwise the type itself */ def repeatedToSingle(implicit ctx: Context): Type = this match { case tp @ ExprType(tp1) => tp.derivedExprType(tp1.repeatedToSingle) - case _ => if (isRepeatedParam) this.argTypesHi.head else this + case _ => if (isRepeatedParam) stripJavaNull.argTypesHi.head else this } /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */ @@ -1430,6 +1505,12 @@ object Types { // ----- misc ----------------------------------------------------------- + /** Create a `JavaNullable` version of this type. */ + def toJavaNullable(implicit ctx: Context): Type = { + if (this.isJavaNullableUnion) this + else OrType(this, defn.JavaNullType) + } + /** Turn type into a function type. * @pre this is a method type without parameter dependencies. * @param dropLast The number of trailing parameters that should be dropped @@ -1692,6 +1773,13 @@ object Types { private[this] var checkedPeriod: Period = Nowhere private[this] var myStableHash: Byte = 0 + // TODO(abeln): remove this hack. + // This is a hack to detect whether `this` is an instance of `NonNullTermRef`, and adjust + // the denotation accordingly. The "adjustment" happens in `computeDenot`, which is currently + // marked as private. + // Overriden in `NonNullTermRef`. + protected val isNonNull: Boolean = false + // Invariants: // (1) checkedPeriod != Nowhere => lastDenotation != null // (2) lastDenotation != null => lastSymbol != null @@ -1797,14 +1885,21 @@ object Types { private def computeDenot(implicit ctx: Context): Denotation = { def finish(d: Denotation) = { - if (d.exists) + if (d.exists) { // Avoid storing NoDenotations in the cache - we will not be able to recover from // them. The situation might arise that a type has NoDenotation in some later // phase but a defined denotation earlier (e.g. a TypeRef to an abstract type // is undefined after erasure.) We need to be able to do time travel back and // forth also in these cases. - setDenot(d) - d + + // TODO(abeln): remove once `isNonNull` is gone. + // If the denotation is computed for the first time, or if it's ever updated, make sure + // that the `info` is non-null. + val d1 = if (isNonNull) d.mapInfo(_.stripNull) else d + + setDenot(d1) + d1 + } else d } def fromDesignator = designator match { @@ -2204,7 +2299,7 @@ object Types { private var myDesignator: Designator) extends NamedType with SingletonType with ImplicitRef { - type ThisType = TermRef + type ThisType >: this.type <: TermRef type ThisName = TermName override def designator: Designator = myDesignator @@ -2255,10 +2350,36 @@ object Types { } final class CachedTermRef(prefix: Type, designator: Designator, hc: Int) extends TermRef(prefix, designator) { + type ThisType = CachedTermRef + assert((prefix ne NoPrefix) || designator.isInstanceOf[Symbol]) myHash = hc } + /** A `TermRef` that, through flow-sensitive type inference, we know is non-null. + * Accordingly, the `info` in its denotation won't be of the form `T|Null`. + * Notice that this class isn't cached, unlike the regular `TermRef`. + */ + final class NonNullTermRef(prefix: Type, designator: Designator) extends TermRef(prefix, designator) { + type ThisType = NonNullTermRef + + // This allows `NamedType` to identify `NonNullTermRef` from all other named types. + override protected val isNonNull: Boolean = true + } + + object NonNullTermRef { + + /** Create a `TermRef` that's just like `tref`, but whose `info` is always non-null. */ + def fromTermRef(tref: TermRef)(implicit ctx: Context): NonNullTermRef = { + val denot = tref.denot.mapInfo(_.stripNull) + val nn = new NonNullTermRef(tref.prefix, denot.symbol) + // We need to set the non-null denotation directly because normally the "non-nullable" denotations + // are created in `computeDenot`, but they _won't_ be computed if the original `tref` _already_ had + // a cached denotation. + nn.withDenot(denot) + } + } + final class CachedTypeRef(prefix: Type, designator: Designator, hc: Int) extends TypeRef(prefix, designator) { assert((prefix ne NoPrefix) || designator.isInstanceOf[Symbol]) myHash = hc @@ -4203,6 +4324,7 @@ object Types { case et: ExprType => true case _ => false } + if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters else NoType case tp: AppliedType => @@ -4218,6 +4340,7 @@ object Types { case _ => NoType } + def isInstantiatable(tp: Type)(implicit ctx: Context): Boolean = zeroParamClass(tp) match { case cinfo: ClassInfo => val selfType = cinfo.selfType.asSeenFrom(tp, cinfo.cls) @@ -4225,15 +4348,22 @@ object Types { case _ => false } - def unapply(tp: Type)(implicit ctx: Context): Option[MethodType] = - if (isInstantiatable(tp)) { - val absMems = tp.abstractTermMembers + + def unapply(tp: Type)(implicit ctx: Context): Option[MethodType] = { + // Strip the nullability from the type (if it exists) before matching + // against the SAM type. This is so that we can use e.g. `Function1[Int, Int]|Null` + // as a prototype for e.g. `(x) => x`. + // See tests/pos/explicit-null-sam-types.scala + val strippedTp = tp.stripNull + if (isInstantiatable(strippedTp)) { + // Ignore the synthetic super accessor: it'll be dealt with in `ExpandSAMs`. + val absMems = strippedTp.abstractTermMembers.filter(!_.symbol.isSuperAccessor) // println(s"absMems: ${absMems map (_.show) mkString ", "}") if (absMems.size == 1) absMems.head.info match { case mt: MethodType if !mt.isParamDependent && - !defn.isImplicitFunctionType(mt.resultType) => - val cls = tp.classSymbol + !defn.isImplicitFunctionType(mt.resultType) => + val cls = strippedTp.classSymbol // Given a SAM type such as: // @@ -4259,7 +4389,7 @@ object Types { mapOver(info.alias) case TypeBounds(lo, hi) => range(atVariance(-variance)(apply(lo)), apply(hi)) - case _ => + case _ => range(defn.NothingType, defn.AnyType) // should happen only in error cases } case _ => @@ -4271,17 +4401,18 @@ object Types { case _ => None } - else if (tp isRef defn.PartialFunctionClass) - // To maintain compatibility with 2.x, we treat PartialFunction specially, - // pretending it is a SAM type. In the future it would be better to merge - // Function and PartialFunction, have Function1 contain a isDefinedAt method - // def isDefinedAt(x: T) = true - // and overwrite that method whenever the function body is a sequence of - // case clauses. + else if (strippedTp isRef defn.PartialFunctionClass) + // To maintain compatibility with 2.x, we treat PartialFunction specially, + // pretending it is a SAM type. In the future it would be better to merge + // Function and PartialFunction, have Function1 contain a isDefinedAt method + // def isDefinedAt(x: T) = true + // and overwrite that method whenever the function body is a sequence of + // case clauses. absMems.find(_.symbol.name == nme.apply).map(_.info.asInstanceOf[MethodType]) else None } else None + } } // ----- TypeMaps -------------------------------------------------------------------- @@ -4725,7 +4856,7 @@ object Types { protected def reapply(tp: Type): Type = apply(tp) } - + /** A range of possible types between lower bound `lo` and upper bound `hi`. * Only used internally in `ApproximatingTypeMap`. */ diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 80377807dedc..d5dda6e38f0a 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -7,6 +7,7 @@ import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ import NameKinds.DefaultGetterName import dotty.tools.dotc.core.tasty.{TastyHeaderUnpickler, TastyReader} +import dotty.tools.dotc.config.Printers import ast.tpd._ import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, IOException } @@ -157,7 +158,7 @@ class ClassfileParser( enterOwnInnerClasses() - classRoot.setFlag(sflags) + classRoot.setFlag(Flags.JavaDefined | sflags) moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags) setPrivateWithin(classRoot, jflags) setPrivateWithin(moduleRoot, jflags) @@ -269,10 +270,13 @@ class ClassfileParser( setPrivateWithin(denot, jflags) denot.info = translateTempPoly(parseAttributes(sym, denot.info)) if (isConstructor) normalizeConstructorInfo() - if ((denot is Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0) denot.info = arrayToRepeated(denot.info) + val old = denot.info + denot.info = JavaNull.nullifyMember(denot.symbol, denot.info) + Printers.nullability.println(s"nullified member type from classfile for ${denot.symbol.name.show} from ${old.show} into ${denot.info.show}") + // seal java enums if (isEnum) { val enumClass = sym.owner.linkedClass diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index d1297623da83..edce1ed858de 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -62,8 +62,9 @@ object Scala2Unpickler { def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { case tp: MethodType => val lastArg = tp.paramInfos.last - assert(lastArg isRef defn.ArrayClass) - val elemtp0 :: Nil = lastArg.baseType(defn.ArrayClass).argInfos + val lastArg1 = lastArg.stripJavaNull + assert(lastArg1 isRef defn.ArrayClass) + val elemtp0 :: Nil = lastArg1.baseType(defn.ArrayClass).argInfos val elemtp = elemtp0 match { case AndType(t1, t2) => // drop intersection with Object for abstract types an parameters in varargs. Erasure can handle them. if (t2.isRef(defn.ObjectClass)) @@ -76,9 +77,11 @@ object Scala2Unpickler { case _ => elemtp0 } + val repParamTp = defn.RepeatedParamType.appliedTo(elemtp) + val lastParamTp = if (lastArg.isJavaNullableUnion) repParamTp.toJavaNullable else repParamTp tp.derivedLambdaType( tp.paramNames, - tp.paramInfos.init :+ defn.RepeatedParamType.appliedTo(elemtp), + tp.paramInfos.init :+ lastParamTp, tp.resultType) case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, arrayToRepeated(tp.resultType)) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index e16d27ce06af..79f9808d2738 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -836,7 +836,7 @@ object JavaParsers { */ val superclazz = Apply(TypeApply( Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), - List(Literal(Constant(null)),Literal(Constant(0)))) + List(Literal(Constant("")),Literal(Constant(0)))) val enumclazz = atPos(start, nameOffset) { TypeDef(name, makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnum) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 09c943697950..98aea0696937 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -317,7 +317,10 @@ object Parsers { accept(SEMI) } - def errorTermTree: Literal = atPos(in.offset) { Literal(Constant(null)) } + /** An error tree that stands for a pattern or an expression */ + def errorTermTree(inPattern: Boolean): Tree = atPos(in.offset) { + Ident(if (inPattern) nme.WILDCARD else nme.???) + } private[this] var inFunReturnType = false private def fromWithinReturnType[T](body: => T): T = { @@ -1457,7 +1460,7 @@ object Parsers { if (isLiteral) literal() else { syntaxErrorOrIncomplete(IllegalStartSimpleExpr(tokenString(in.token))) - errorTermTree + errorTermTree(inPattern = false) } } simpleExprRest(t, canApply) @@ -1749,7 +1752,7 @@ object Parsers { if (isLiteral) literal(inPattern = true) else { syntaxErrorOrIncomplete(IllegalStartOfSimplePattern()) - errorTermTree + errorTermTree(inPattern = true) } } diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 0f0a0d36bf53..fb999ccf383f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -130,7 +130,7 @@ object MarkupParsers { try handle.parseAttribute(Position(start, curOffset, mid), tmp) catch { case e: RuntimeException => - errorAndResult("error parsing attribute value", parser.errorTermTree) + errorAndResult("error parsing attribute value", parser.errorTermTree(inPattern = false)) } case '{' => @@ -330,7 +330,7 @@ object MarkupParsers { finally parser.in resume Tokens.XMLSTART if (output == null) - parser.errorTermTree + parser.errorTermTree(inPattern = false) else output } diff --git a/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala b/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala index bae91bf62417..f3305ece0a3c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala @@ -34,7 +34,7 @@ object CollectNullableFields { * - belongs to a non trait-class * - is private[this] * - is not lazy - * - its type is nullable + * - its type is nullable after erasure * - is only used in a lazy val initializer * - defined in the same class as the lazy val */ @@ -65,7 +65,9 @@ class CollectNullableFields extends MiniPhase { !sym.is(Lazy) && !sym.owner.is(Trait) && sym.initial.is(PrivateLocal) && - sym.info.widenDealias.typeSymbol.isNullableClass + // We need `isNullableClassAfterErasure` and not `isNullable` because + // we care about the values as present in the JVM. + sym.info.widenDealias.typeSymbol.isNullableClassAfterErasure if (isNullablePrivateField) nullability.get(sym) match { diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 625be403a508..ef3b6833d65a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -140,12 +140,20 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => info = toJavaVarArgs(ddef.symbol.info)).enteredAfter(thisPhase).asTerm val bridgeDef = polyDefDef(bridge, trefs => vrefss => { val (vrefs :+ varArgRef) :: vrefss1 = vrefss + // If the varargs comes from Java, it'll have a type of the form `Array[T]|JavaNull`, so to + // get the element type we need to strip away the nullability. + val arrayTpe = varArgRef.tpe.widen.stripNull + // Since we're overriding a Java varargs, the user will pass in a `Array[T]|JavaNull`. + // But our Scala-defined varargs takes in a `Seq[T]`, so we need to cast away the nullability. + // This means that if the user passes `null` to the Scala method it'll be allowed by the + // type system, but will throw at runtime. + val varArgArg = varArgRef.ensureConforms(arrayTpe) // Can't call `.argTypes` here because the underlying array type is of the // form `Array[_ <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`. - val elemtp = varArgRef.tpe.widen.argInfos.head + val elemtp = arrayTpe.argInfos.head ref(original.termRef) .appliedToTypes(trefs) - .appliedToArgs(vrefs :+ tpd.wrapArray(varArgRef, elemtp)) + .appliedToArgs(vrefs :+ tpd.wrapArray(varArgArg, elemtp)) .appliedToArgss(vrefss1) }) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index bafea790faba..cf23df7f7895 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -54,8 +54,12 @@ class Erasure extends Phase with DenotTransformer { // After erasure, all former Any members are now Object members val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info val extendedScope = decls.cloneScope - for (decl <- defn.AnyClass.classInfo.decls) - if (!decl.isConstructor) extendedScope.enter(decl) + def addDecls(cls: ClassSymbol) = { + for (decl <- cls.classInfo.decls) + if (!decl.isConstructor) extendedScope.enter(decl) + } + addDecls(defn.AnyClass) + addDecls(defn.RefEqClass) ref.copySymDenotation( info = transformInfo(ref.symbol, ClassInfo(pre, defn.ObjectClass, ps, extendedScope, selfInfo)) @@ -68,7 +72,7 @@ class Erasure extends Phase with DenotTransformer { defn.ObjectClass.primaryConstructor else oldSymbol val oldOwner = ref.owner - val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner + val newOwner = if ((oldOwner eq defn.AnyClass) || (oldOwner eq defn.RefEqClass)) defn.ObjectClass else oldOwner val oldInfo = ref.info val newInfo = transformInfo(oldSymbol, oldInfo) val oldFlags = ref.flags @@ -399,7 +403,7 @@ object Erasure { def mapOwner(sym: Symbol): Symbol = { def recur(owner: Symbol): Symbol = if (defn.erasedToObject.contains(owner)) { - assert(sym.isConstructor, s"${sym.showLocated}") +// assert(sym.isConstructor, s"${sym.showLocated}") defn.ObjectClass } else if (defn.isSyntheticFunctionClass(owner)) defn.erasedFunctionClass(owner) diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 8e207d31ef5f..904667af8a84 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.reporting.diagnostic.messages.TypeMismatch import dotty.tools.dotc.util.Positions.Position /** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes. - * These fall into five categories + * These fall into six categories * * 1. Partial function closures, we need to generate isDefinedAt and applyOrElse methods for these. * 2. Closures implementing non-trait classes @@ -19,6 +19,7 @@ import dotty.tools.dotc.util.Positions.Position * 4. Closures that implement traits which run initialization code. * 5. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be * (1) superaccessors, (2) outer references, (3) accessors for fields. + * 6. Nullable functions: e.g. `(Int => Int)|Null` * * However, implicit function types do not count as SAM types. */ @@ -36,19 +37,29 @@ class ExpandSAMs extends MiniPhase { tpt.tpe match { case NoType => tree // it's a plain function - case tpe if defn.isImplicitFunctionType(tpe) => - tree - case tpe @ SAMType(_) if tpe.isRef(defn.PartialFunctionClass) => - val tpe1 = checkRefinements(tpe, fn.pos) - toPartialFunction(tree, tpe1) - case tpe @ SAMType(_) if isPlatformSam(tpe.classSymbol.asClass) => - checkRefinements(tpe, fn.pos) - tree - case tpe => - val tpe1 = checkRefinements(tpe, fn.pos) - val Seq(samDenot) = tpe1.abstractTermMembers.filter(!_.symbol.isSuperAccessor) - cpy.Block(tree)(stats, + case SAMType(_) => + // If the type is a SAM type, it's also possibly nullable. However, here we're desugaring an + // expression of the form `def fun() = {...}; closure(fun)`, so the desugaring should use the + // more precise non-nullable type for the literal. + // TODO(abeln): this makes handling closures typed as nullable go through the slow path that doesn't + // use SAM types. + // In other words, `val x: Int => Int = (x) => x` and `val x: (Int => Int)|Null = (x) => x` both typecheck, + // but the former uses the platform SAM types, vs the latter which uses an anonymous class. + val tpe = tpt.tpe.stripNull + if (defn.isImplicitFunctionType(tpe)) { + tree + } else if (tpe.isRef(defn.PartialFunctionClass)) { + val tpe1 = checkRefinements(tpe, fn.pos) + toPartialFunction(tree, tpe1) + } else if (isPlatformSam(tpe.classSymbol.asClass)) { + checkRefinements(tpe, fn.pos) + tree + } else { + val tpe1 = checkRefinements(tpe, fn.pos) + val Seq(samDenot) = tpe1.abstractTermMembers.filter(!_.symbol.isSuperAccessor) + cpy.Block(tree)(stats, AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil)) + } } case _ => tree diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 6717b3499f05..e2aea1bf6994 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -50,10 +50,12 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { tree match { case Select(qual, name) if !name.is(OuterSelectName) && tree.symbol.exists => + // JavaNull is already special-cased in the Typer, but needs to be handled here as well. + val qualTpe = if (qual.tpe.isJavaNullableUnion) qual.tpe.stripJavaNull else qual.tpe assert( - qual.tpe.derivesFrom(tree.symbol.owner) || - tree.symbol.is(JavaStatic) && qual.tpe.derivesFrom(tree.symbol.enclosingClass), - i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe} in $tree") + qualTpe.derivesFrom(tree.symbol.owner) || + tree.symbol.is(JavaStatic) && qualTpe.derivesFrom(tree.symbol.enclosingClass), + i"non member selection of ${tree.symbol.showLocated} from ${qualTpe} in $tree") case _: TypeTree => case _: Import | _: NamedArg | _: TypTree => assert(false, i"illegal tree: $tree") diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index e470af9c43cb..2b59ccd209bc 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -145,6 +145,7 @@ object PatternMatcher { case class ReturnPlan(var label: TermSymbol) extends Plan case class SeqPlan(var head: Plan, var tail: Plan) extends Plan case class ResultPlan(var tree: Tree) extends Plan + case object NoOpPlan extends Plan object TestPlan { def apply(test: Test, sym: Symbol, pos: Position, ons: Plan): TestPlan = @@ -338,9 +339,19 @@ object PatternMatcher { val mt @ MethodType(_) = extractor.tpe.widen var unapp = extractor.appliedTo(ref(scrutinee).ensureConforms(mt.paramInfos.head)) if (implicits.nonEmpty) unapp = unapp.appliedToArgs(implicits) - val unappPlan = unapplyPlan(unapp, args) - if (scrutinee.info.isNotNull || nonNull(scrutinee)) unappPlan - else TestPlan(NonNullTest, scrutinee, tree.pos, unappPlan) + val scrutineeTpe = scrutinee.info.widenDealias + if (scrutineeTpe.isBottomType || scrutineeTpe.isNullType) { + // If the value being matched against has type `Nothing`, then the unapply code + // will never execute. + // If it has type `Null`, then the `NonNullTest` below guarantees that the unapply code + // won't execute either. + // So we don't need a plan in this case. + NoOpPlan + } else { + val unappPlan = unapplyPlan(unapp, args) + if (scrutinee.info.isNotNull || nonNull(scrutinee)) unappPlan + else TestPlan(NonNullTest, scrutinee, tree.pos, unappPlan) + } case Bind(name, body) => if (name == nme.WILDCARD) patternPlan(scrutinee, body, onSuccess) else { @@ -420,6 +431,7 @@ object PatternMatcher { case plan: ReturnPlan => apply(plan) case plan: SeqPlan => apply(plan) case plan: ResultPlan => plan + case NoOpPlan => NoOpPlan } } @@ -696,6 +708,7 @@ object PatternMatcher { private def canFallThrough(plan: Plan): Boolean = plan match { case _:ReturnPlan | _:ResultPlan => false case _:TestPlan | _:LabeledPlan => true + case NoOpPlan => true case LetPlan(_, body) => canFallThrough(body) case SeqPlan(_, tail) => canFallThrough(tail) } @@ -859,6 +872,9 @@ object PatternMatcher { case ResultPlan(tree) => if (tree.tpe <:< defn.NothingType) tree // For example MatchError else Return(tree, ref(resultLabel)) + case NoOpPlan => + // TODO(abeln): optimize away + Literal(Constant(true)) } } @@ -897,6 +913,8 @@ object PatternMatcher { showPlan(tail) case ResultPlan(tree) => sb.append(tree.show) + case NoOpPlan => + sb.append("NoOp") } } showPlan(plan) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 0b9dde117732..413bf0c3d7e4 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -117,7 +117,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) { val ioob = defn.IndexOutOfBoundsException.typeRef // Second constructor of ioob that takes a String argument def filterStringConstructor(s: Symbol): Boolean = s.info match { - case m: MethodType if s.isConstructor => m.paramInfos == List(defn.StringType) + case m: MethodType if s.isConstructor && m.paramInfos.size == 1 => m.paramInfos.head.stripJavaNull == defn.StringType case _ => false } val constructor = ioob.typeSymbol.info.decls.find(filterStringConstructor _).asTerm diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 42c2580f78c4..959f242245b3 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -289,15 +289,23 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { private val scalaNilType = ctx.requiredModuleRef("scala.collection.immutable.Nil") private val scalaConsType = ctx.requiredClassRef("scala.collection.immutable.::") - private val nullType = ConstantType(Constant(null)) - private val nullSpace = Typ(nullType) + private val constantNullType = ConstantType(Constant(null)) + private val constantNullSpace = Typ(constantNullType) + + /** Does the given tree stand for the literal `null`? */ + def isNullLit(tree: Tree): Boolean = tree match { + case Literal(Constant(null)) => true + case _ => false + } + + /** Does the given space contain just the value `null`? */ + def isNullSpace(space: Space): Boolean = space match { + case Typ(tpe, _) => tpe =:= constantNullType || tpe.isRefToNull + case Or(spaces) => spaces.forall(isNullSpace) + case _ => false + } override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = { - // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1) - if (tp1 == nullType || tp2 == nullType) { - // Since projections of types don't include null, intersection with null is empty. - return Empty - } val and = AndType(tp1, tp2) // Then, no leaf of the and-type tree `and` is a subtype of `and`. val res = inhabited(and) @@ -328,7 +336,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { Typ(ConstantType(c), false) case _: BackquotedIdent => Typ(pat.tpe, false) case Ident(nme.WILDCARD) => - Or(Typ(pat.tpe.stripAnnots, false) :: nullSpace :: Nil) + Or(Typ(pat.tpe.stripAnnots, false) :: constantNullSpace :: Nil) case Ident(_) | Select(_, _) => Typ(pat.tpe.stripAnnots, false) case Alternative(trees) => Or(trees.map(project(_))) @@ -389,7 +397,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { - val res = (tp1 != nullType || tp2 == nullType) && tp1 <:< tp2 + val res = tp1 <:< tp2 debug.println(s"${tp1} <:< ${tp2} = $res") res } @@ -936,11 +944,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (!redundancyCheckable(sel)) return - val targetSpace = - if (selTyp.classSymbol.isPrimitiveValueClass) - Typ(selTyp, true) - else - Or(Typ(selTyp, true) :: nullSpace :: Nil) + val targetSpace = Typ(selTyp, true) // in redundancy check, take guard as false in order to soundly approximate def projectPrevCases(cases: List[CaseDef]): Space = @@ -949,11 +953,6 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { else Empty }.reduce((a, b) => Or(List(a, b))) - def isNull(tree: Tree): Boolean = tree match { - case Literal(Constant(null)) => true - case _ => false - } - (1 until cases.length).foreach { i => val prevs = projectPrevCases(cases.take(i)) @@ -977,9 +976,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } // if last case is `_` and only matches `null`, produce a warning - if (i == cases.length - 1 && !isNull(pat) ) { + if (i == cases.length - 1 && !isNullLit(pat) ) { simplify(minus(covered, prevs)) match { - case Typ(`nullType`, _) => + case space if isNullSpace(space) => ctx.warning(MatchCaseOnlyNullWarning(), pat.pos) case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 438249f89124..844f9f149e42 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -23,12 +23,12 @@ import Inferencing._ import collection.mutable import config.Printers.{overload, typr, unapp} import TypeApplications._ - import reporting.diagnostic.Message import reporting.trace import Constants.{Constant, IntTag, LongTag} import dotty.tools.dotc.reporting.diagnostic.messages.{NotAnExtractor, UnapplyInvalidNumberOfArguments} import Denotations.SingleDenotation +import dotty.tools.dotc.core.FlowFacts.Inferred object Applications { import tpd._ @@ -531,7 +531,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case arg :: Nil if isVarArg(arg) => addTyped(arg, formal) case _ => - val elemFormal = formal.widenExpr.argTypesLo.head + val elemFormal = formal.widenExpr.repeatedToSingle val typedArgs = harmonic(harmonizeArgs, elemFormal)(args.map(typedArg(_, elemFormal))) typedArgs.foreach(addArg(_, elemFormal)) @@ -784,19 +784,31 @@ trait Applications extends Compatibility { self: Typer with Dynamic => typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt") /** Type application where arguments come from prototype, and no implicits are inserted */ - def simpleApply(fun1: Tree, proto: FunProto)(implicit ctx: Context): Tree = - methPart(fun1).tpe match { - case funRef: TermRef => - val app = - if (proto.allArgTypesAreCurrent()) - new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) - else - new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) - convertNewGenericArray(app.result) - case _ => - handleUnexpectedFunType(tree, fun1) + def simpleApply(fun1: Tree, proto: FunProto)(implicit ctx: Context): Tree = { + // TODO(abeln): we're re-doing work here by recomputing what's implies by the lhs of the comparison. + // e.g. in `A && B && C && D`, we'll recompute the facts implied by `A && B` twice. + // Find a more-efficient way to do this. + val ctx1 = FlowFacts.propagateWithinCond(fun1) + + // Separate into a function so we can pass the updated context. + def proc(implicit ctx: Context) = { + proto.withContext(ctx).asInstanceOf[FunProto] + methPart(fun1).tpe match { + case funRef: TermRef => + val app = + if (proto.allArgTypesAreCurrent()) + new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) + else + new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) + convertNewGenericArray(app.result) + case _ => + handleUnexpectedFunType(tree, fun1) + } } + proc(ctx1) + } + /** Try same application with an implicit inserted around the qualifier of the function * part. Return an optional value to indicate success. */ diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index e9f9d6d5ea50..11fc5ddca770 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -79,8 +79,9 @@ object ErrorReporting { def takesNoParamsStr(tree: Tree, kind: String): String = if (tree.tpe.widen.exists) i"${exprStr(tree)} does not take ${kind}parameters" - else + else { i"undefined: $tree # ${tree.uniqueId}: ${tree.tpe.toString} at ${ctx.phase}" + } def patternConstrStr(tree: Tree): String = ??? diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index aef040c5df78..63ed70fab196 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -604,8 +604,19 @@ trait Implicits { self: Typer => SelectionProto(name, memberProto, compat, privateOK = false) case tp => tp } - try inferImplicit(adjust(to), from, from.pos) - catch { + try { + val to1 = adjust(to) + inferImplicit(to1, from, from.pos) match { + case err: SearchFailure if !err.isAmbiguous && from.tpe.isJavaNullableUnion => + // TODO(abeln): is this worth keeping? + // When looking for an implicit conversion from A|JavaNull -> B, if we + // can't find it, then also look for an implicit conversion from A -> B. + // This is done to ease Java interop. + val from1 = from.ensureConforms(from.tpe.stripJavaNull) + inferImplicit(to1, from1, from.pos) + case res => res + } + } catch { case ex: AssertionError => implicits.println(s"view $from ==> $to") implicits.println(ctx.typerState.constraint.show) @@ -717,8 +728,11 @@ trait Implicits { self: Typer => inferImplicit(defn.EqType.appliedTo(tp, tp), EmptyTree, pos).isSuccess def validEqAnyArgs(tp1: Type, tp2: Type)(implicit ctx: Context) = { + lazy val eitherIsNull = tp1.isRef(defn.NullClass) || tp2.isRef(defn.NullClass) List(tp1, tp2).foreach(fullyDefinedType(_, "eqAny argument", pos)) - assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) + // If either of the types is `Null`, then we only want to generate the fallback `Eq` + // the other type is a reference type. + assumedCanEqual(tp1, tp2) || !eitherIsNull && !hasEq(tp1) && !hasEq(tp2) } inferImplicit(formal, EmptyTree, pos)(ctx) match { @@ -851,7 +865,10 @@ trait Implicits { self: Typer => if (ltp.isRef(defn.NullClass)) rtp else if (rtp.isRef(defn.NullClass)) ltp else NoType - + // Even though classes deriving from `AnyRef` are non-nullable, we still + // allow testing them for equality against null. + // This is because nulls can still sneak in without detection by the type + // system (e.g. via Java), so checking for null is a useful escape hatch. (other ne NoType) && !other.derivesFrom(defn.AnyValClass) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index fa881495dcf9..728e1a0e23d2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -19,6 +19,7 @@ import Annotations._ import Inferencing._ import transform.ValueClasses._ import reporting.diagnostic.messages._ +import dotty.tools.dotc.config.Printers trait NamerContextOps { this: Context => import NamerContextOps._ @@ -136,9 +137,11 @@ trait NamerContextOps { this: Context => if (params.isEmpty) (false, false) else (params.head is Implicit, params.head is Erased) val make = MethodType.maker(isJava, isImplicit, isErased) + // Following Scalac, Namer needs to convert Java method parameters + // of type j.l.Object|JavaNull to s.Any. if (isJava) for (param <- params) - if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType + if (param.info.stripNull.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType make.fromSymbols(params, resultType) } if (typeParams.nonEmpty) PolyType.fromParams(typeParams.asInstanceOf[List[TypeSymbol]], monotpe) @@ -1176,7 +1179,14 @@ class Namer { typer: Typer => case _ => WildcardType } - paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe) + val memTpe = paramFn(checkSimpleKinded(typedAheadType(mdef.tpt, tptProto)).tpe) + if (mdef.mods.is(JavaDefined)) { + val nullifiedTpe = JavaNull.nullifyMember(sym, memTpe) + Printers.nullability.println(s"nullified member type from source for ${sym.name.show} from ${memTpe.show} into ${nullifiedTpe.show}") + nullifiedTpe + } else { + memTpe + } } /** The type signature of a DefDef with given symbol */ diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9c3af1eef743..7c5bcda9ce94 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -42,7 +42,11 @@ object ProtoTypes { } private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match { - case _: OrType => true + case orTpe: OrType => + // If either side of the union is the `Null` type, it should be safe to not disregard the prototype. + // This is because checking for compatibility against e.g. `OrType(T1, Null)` will not generate incompatible + // constraints (because `Null` is atomic). + !orTpe.isNullableUnion case pt => pt.isRef(defn.UnitClass) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 833e25a55976..fd228f0636c7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -36,6 +36,7 @@ import util.Stats.{record, track} import config.Printers.{gadts, typr} import rewrites.Rewrites.patch import NavigateAST._ +import dotty.tools.dotc.core.FlowFacts.{Inferred, NonNullSet} import transform.SymUtils._ import transform.TypeUtils._ import reporting.trace @@ -396,11 +397,13 @@ class Typer extends Namer } else errorType(new MissingIdent(tree, kind, name.show), tree.pos) - val tree1 = ownType match { - case ownType: NamedType if !prefixIsElidable(ownType) => - ref(ownType).withPos(tree.pos) + val ownType1 = FlowFacts.refineType(ownType) + + val tree1 = ownType1 match { + case ownType1: NamedType if !prefixIsElidable(ownType1) => + ref(ownType1).withPos(tree.pos) case _ => - tree.withType(ownType) + tree.withType(ownType1) } checkStableIdentPattern(tree1, pt) @@ -420,8 +423,11 @@ class Typer extends Namer tree } - private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select = - checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt) + private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select = { + val select = assignType(cpy.Select(tree)(qual, tree.name), qual) + val select1 = select.withType(FlowFacts.refineType(select.tpe)) + checkValue(select1, pt) + } def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { @@ -727,9 +733,12 @@ class Typer extends Namer def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") { if (tree.isInline) checkInInlineContext("inline if", tree.pos) val cond1 = typed(tree.cond, defn.BooleanType) + val Inferred(ifTrue, ifFalse) = FlowFacts.inferNonNull(cond1) + val thenCtx = ctx.fresh.addNonNullFacts(ifTrue) + val elseCtx = ctx.fresh.addNonNullFacts(ifFalse) val thenp2 :: elsep2 :: Nil = harmonic(harmonize, pt) { - val thenp1 = typed(tree.thenp, pt.notApplied) - val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied) + val thenp1 = typed(tree.thenp, pt.notApplied)(thenCtx) + val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)(elseCtx) thenp1 :: elsep1 :: Nil } assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) @@ -1178,7 +1187,7 @@ class Typer extends Namer } def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Tree = track("typedThrow") { - val expr1 = typed(tree.expr, defn.ThrowableType) + val expr1 = typed(tree.expr, OrType(defn.ThrowableType, defn.NullType)) Throw(expr1).withPos(tree.pos) } diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 90cd8dbf757e..c43b064b77b2 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -10,3 +10,9 @@ t3249 t3486 t3612.scala typelevel0.scala +Transactions.scala +explicit-null-flow.scala +explicit-null-flow2.scala +explicit-null-flow3.scala +explicit-null-flow4.scala +t1107a.scala diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index a2fbf589a9c8..1e39bdc96770 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -8,4 +8,3 @@ puzzle.scala implicitMatch.scala typeclass-derivation1.scala typeclass-derivation2.scala - diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index aa1c42f1a07e..48aa0dbfc8c3 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -6,3 +6,6 @@ t7374 tuples1.scala tuples1a.scala typeclass-derivation2.scala + +# need support for flow-sensitive type inference +constant-optimization.scala diff --git a/library/src-bootstrapped/scala/NonNull.scala b/library/src-bootstrapped/scala/NonNull.scala new file mode 100644 index 000000000000..11c80789f046 --- /dev/null +++ b/library/src-bootstrapped/scala/NonNull.scala @@ -0,0 +1,20 @@ +package scala + +object NonNull { + implicit class NonNull[T](x: T|Null) extends AnyVal { + def nn: T = if (x == null) { + throw new NullPointerException("tried to cast away nullability, but value is null") + } else { + x.asInstanceOf[T] + } + } + + object ArrayConversions { + // TODO(abeln) add additional versions of these a la FunctionN? + implicit def toNullable1[T](a: Array[T]): Array[T|Null] = a.asInstanceOf[Array[T|Null]] + implicit def toNullable2[T](a: Array[Array[T]]): Array[Array[T|Null]|Null] = a.asInstanceOf[Array[Array[T|Null]|Null]] + + implicit def fromNullable1[T](a: Array[T|Null]): Array[T] = a.asInstanceOf[Array[T]] + implicit def fromNullable2[T](a: Array[Array[T|Null]|Null]): Array[Array[T]] = a.asInstanceOf[Array[Array[T]]] + } +} diff --git a/tests/generic-java-signatures/andTypes.scala b/tests/generic-java-signatures/andTypes.scala index b4557fd56b6f..042b416d0e1d 100644 --- a/tests/generic-java-signatures/andTypes.scala +++ b/tests/generic-java-signatures/andTypes.scala @@ -10,7 +10,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo].getDeclaredMethod("foo").getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/arrayBound.scala b/tests/generic-java-signatures/arrayBound.scala index d4f24e7f7ca6..b6af9fcd9352 100644 --- a/tests/generic-java-signatures/arrayBound.scala +++ b/tests/generic-java-signatures/arrayBound.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _, _, _, _]].getTypeParameters() tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/boundParameters.scala b/tests/generic-java-signatures/boundParameters.scala index aa80e62d6cb2..792e80794b51 100644 --- a/tests/generic-java-signatures/boundParameters.scala +++ b/tests/generic-java-signatures/boundParameters.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _, _]].getTypeParameters() tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/boundsInterfaces.scala b/tests/generic-java-signatures/boundsInterfaces.scala index 2b0daaa5309a..2597f218afb9 100644 --- a/tests/generic-java-signatures/boundsInterfaces.scala +++ b/tests/generic-java-signatures/boundsInterfaces.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/erased.scala b/tests/generic-java-signatures/erased.scala index a9731cc748fb..88f285e51df8 100644 --- a/tests/generic-java-signatures/erased.scala +++ b/tests/generic-java-signatures/erased.scala @@ -4,11 +4,12 @@ object MyErased { object Test { def main(args: Array[String]): Unit = { - val f1 = MyErased.getClass.getMethods.find(_.getName.endsWith("f1")).get + val f1 = MyErased.getClass.getMethods.find(_.nn.getName.endsWith("f1")).get.nn val tParams = f1.getTypeParameters println(f1.toGenericString) tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/higherKinded.scala b/tests/generic-java-signatures/higherKinded.scala index 624f122676fe..887651d43abf 100644 --- a/tests/generic-java-signatures/higherKinded.scala +++ b/tests/generic-java-signatures/higherKinded.scala @@ -5,7 +5,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _, _, _, _]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/i3411.scala b/tests/generic-java-signatures/i3411.scala index b8c4b9f47a98..acbadb6aecb0 100644 --- a/tests/generic-java-signatures/i3411.scala +++ b/tests/generic-java-signatures/i3411.scala @@ -4,7 +4,7 @@ object Foo { object Test { def main(args: Array[String]): Unit = { - val f1 = Foo.getClass.getMethods.find(_.getName.endsWith("foo")).get - println(f1.toGenericString) + val f1 = Foo.getClass.getMethods.find(_.nn.getName.endsWith("foo")).get + println(f1.nn.toGenericString) } } diff --git a/tests/generic-java-signatures/i3476.scala b/tests/generic-java-signatures/i3476.scala index 4af0b77b98a5..9404e478d2a8 100644 --- a/tests/generic-java-signatures/i3476.scala +++ b/tests/generic-java-signatures/i3476.scala @@ -1,9 +1,9 @@ object Test { def hasGenericSignature(cls: Class[_], methName: String): Boolean = { - cls.getDeclaredMethods().find(_.getName.contains(methName)) match { + cls.getDeclaredMethods().find(_.nn.getName.contains(methName)) match { case None => throw new NoSuchMethodError(s"No $methName in ${cls.getName}") - case Some(meth) => meth.getTypeParameters.nonEmpty + case Some(meth) => meth.nn.getTypeParameters.nonEmpty } } diff --git a/tests/generic-java-signatures/i4248.scala b/tests/generic-java-signatures/i4248.scala index 91b34cd41a52..e715b6cb17dd 100644 --- a/tests/generic-java-signatures/i4248.scala +++ b/tests/generic-java-signatures/i4248.scala @@ -4,7 +4,7 @@ object Foo { object Test { def main(args: Array[String]): Unit = { - val f1 = Foo.getClass.getMethods.find(_.getName.endsWith("foo")).get + val f1 = Foo.getClass.getMethods.find(_.nn.getName.endsWith("foo")).get.nn println(f1.toGenericString) } } diff --git a/tests/generic-java-signatures/invalidNames.scala b/tests/generic-java-signatures/invalidNames.scala index 9dbf9da3bdb6..21961070bcff 100644 --- a/tests/generic-java-signatures/invalidNames.scala +++ b/tests/generic-java-signatures/invalidNames.scala @@ -4,7 +4,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_]].getTypeParameters() tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/lowerBoundClass.scala b/tests/generic-java-signatures/lowerBoundClass.scala index 44a0e9cf7f97..660b983d1d77 100644 --- a/tests/generic-java-signatures/lowerBoundClass.scala +++ b/tests/generic-java-signatures/lowerBoundClass.scala @@ -3,7 +3,7 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + println(tp.nn.getName + " <: " + tp.nn.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/mangledNames.scala b/tests/generic-java-signatures/mangledNames.scala index b57aff3d4018..4a2ad4db3a0b 100644 --- a/tests/generic-java-signatures/mangledNames.scala +++ b/tests/generic-java-signatures/mangledNames.scala @@ -4,7 +4,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_]].getTypeParameters() tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/primitiveArrayBound.scala b/tests/generic-java-signatures/primitiveArrayBound.scala index 21efdbb56c95..d7904f49ce8f 100644 --- a/tests/generic-java-signatures/primitiveArrayBound.scala +++ b/tests/generic-java-signatures/primitiveArrayBound.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName.nn + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/primitives.scala b/tests/generic-java-signatures/primitives.scala index 8f75309096fc..c497e6ce8f2e 100644 --- a/tests/generic-java-signatures/primitives.scala +++ b/tests/generic-java-signatures/primitives.scala @@ -11,7 +11,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _, _, _, _, _, _, _, _]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds().map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds().map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/simple.scala b/tests/generic-java-signatures/simple.scala index b8c3be1bffd9..310f4e8b4004 100644 --- a/tests/generic-java-signatures/simple.scala +++ b/tests/generic-java-signatures/simple.scala @@ -2,6 +2,6 @@ class Foo[T, U, LongerName] object Test { def main(args: Array[String]): Unit = { val typeParams = classOf[Foo[_, _, _]].getTypeParameters - typeParams.foreach(tp => println(tp.getName)) + typeParams.foreach(tp => println(tp.nn.getName)) } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/simpleBoundParameters.scala b/tests/generic-java-signatures/simpleBoundParameters.scala index beb99cba299a..e231cfca5a4c 100644 --- a/tests/generic-java-signatures/simpleBoundParameters.scala +++ b/tests/generic-java-signatures/simpleBoundParameters.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_]].getTypeParameters() tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/generic-java-signatures/valueClassBound.scala b/tests/generic-java-signatures/valueClassBound.scala index 76711dd500a9..fba7b2741a67 100644 --- a/tests/generic-java-signatures/valueClassBound.scala +++ b/tests/generic-java-signatures/valueClassBound.scala @@ -4,7 +4,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds.map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds.map(_.nn.getTypeName).mkString(", ")) } } } diff --git a/tests/generic-java-signatures/wildcards.scala b/tests/generic-java-signatures/wildcards.scala index ee7b71f5b612..b1679d4299e6 100644 --- a/tests/generic-java-signatures/wildcards.scala +++ b/tests/generic-java-signatures/wildcards.scala @@ -3,7 +3,8 @@ object Test { def main(args: Array[String]): Unit = { val tParams = classOf[Foo[_, _, _]].getTypeParameters tParams.foreach { tp => - println(tp.getName + " <: " + tp.getBounds().map(_.getTypeName).mkString(", ")) + val tp1 = tp.nn + println(tp1.getName + " <: " + tp1.getBounds().map(_.nn.getTypeName).mkString(", ")) } } -} \ No newline at end of file +} diff --git a/tests/neg-custom-args/isInstanceOf/t2755.scala b/tests/neg-custom-args/isInstanceOf/t2755.scala index 9073e9253098..bcf261f6d63d 100644 --- a/tests/neg-custom-args/isInstanceOf/t2755.scala +++ b/tests/neg-custom-args/isInstanceOf/t2755.scala @@ -10,7 +10,7 @@ object Test { case x: Array[_] => 6 case _ => 7 } - def f2(a: Array[_]) = a match { + def f2(a: Array[_]|Null) = a match { case x: Array[Int] => x(0) case x: Array[Double] => 2 case x: Array[Float] => x.sum.toInt @@ -19,7 +19,7 @@ object Test { case x: Array[_] => 6 case _ => 7 // error: only null is matched } - def f3[T](a: Array[T]) = a match { + def f3[T](a: Array[T]|Null) = a match { case x: Array[Int] => x(0) case x: Array[Double] => 2 case x: Array[Float] => x.sum.toInt diff --git a/tests/neg-tailcall/tailrec-2.scala b/tests/neg-tailcall/tailrec-2.scala index b6683251f0b0..bb67cf351dbb 100644 --- a/tests/neg-tailcall/tailrec-2.scala +++ b/tests/neg-tailcall/tailrec-2.scala @@ -8,13 +8,13 @@ sealed abstract class Super[+A] { class Bop1[+A](val element: A) extends Super[A] { @tailrec final def f[B >: A](mem: List[B]): List[B] = - (null: Super[A]).f(mem) // error: recursive call targeting a supertype + (??? : Super[A]).f(mem) // error: recursive call targeting a supertype @tailrec final def f1[B >: A](mem: List[B]): List[B] = this.g(mem) // error: TailRec optimisation not applicable } // These succeed class Bop2[+A](val element: A) extends Super[A] { - @tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Bop2[A]).f(mem) + @tailrec final def f[B >: A](mem: List[B]): List[B] = (??? : Bop2[A]).f(mem) } object Bop3 extends Super[Nothing] { @tailrec final def f[B](mem: List[B]): List[B] = (??? : Bop3.type).f(mem) diff --git a/tests/neg/equality.scala b/tests/neg/equality.scala index 595d1de6564f..570be93d935d 100644 --- a/tests/neg/equality.scala +++ b/tests/neg/equality.scala @@ -58,11 +58,22 @@ object equality { 1 == true // error + // TODO(abeln): double-check that this is the behaviour we want + /* null == true // OK by eqProxy or eqJBoolSBool true == null // OK by eqSBoolJBool null == 1 // OK by eqProxy or eqNumInt 1 == null // OK by eqIntNum - + */ + null == true // error + true == null // error + null == 1 // error + 1 == null // error + { + val x: AnyVal = ??? + if (x == null) {} // error + def foo[T <: AnyVal](x: T) = if (x != null) {} // error + } class Fruit diff --git a/tests/neg/explicit-null-array-src/J.java b/tests/neg/explicit-null-array-src/J.java new file mode 100644 index 000000000000..80fda83e89d7 --- /dev/null +++ b/tests/neg/explicit-null-array-src/J.java @@ -0,0 +1,3 @@ +class J { + void foo(String[] ss) {} +} diff --git a/tests/neg/explicit-null-array-src/S.scala b/tests/neg/explicit-null-array-src/S.scala new file mode 100644 index 000000000000..3796bab79970 --- /dev/null +++ b/tests/neg/explicit-null-array-src/S.scala @@ -0,0 +1,10 @@ +class S { + + val j = new J() + val x: Array[String] = ??? + j.foo(x) // error: expected Array[String|Null] but got Array[String] + + val x2: Array[String|Null] = ??? + j.foo(x2) // ok + j.foo(null) // ok +} diff --git a/tests/neg/explicit-null-default.scala b/tests/neg/explicit-null-default.scala new file mode 100644 index 000000000000..fe115861e926 --- /dev/null +++ b/tests/neg/explicit-null-default.scala @@ -0,0 +1,13 @@ + +class Foo { + val x: String = null // error: String is non-nullable + + def foo(x: String): String = "x" + + val y = foo(null) // error: String argument is non-nullable + + val z: String = foo("hello") + + class Bar + val b: Bar = null // error: user-created classes are also non-nullable +} diff --git a/tests/neg/explicit-null-eq.scala b/tests/neg/explicit-null-eq.scala new file mode 100644 index 000000000000..970f4ec811a8 --- /dev/null +++ b/tests/neg/explicit-null-eq.scala @@ -0,0 +1,26 @@ +// Test what can be compared for equality against null. +class Foo { + // Null itself + val x0: Null = null + x0 == null + null == x0 + null == null + + // Nullable types: OK + val x1: String|Null = null + x1 == null + null == x1 + + // Reference types, even non-nullable ones: OK. + // Allowed as an escape hatch. + val x2: String = "hello" + x2 != null + x2 == null + null == x2 + + // Value types: not allowed. + 1 == null // error + null == 1 // error + true == null // error + null == true // error +} diff --git a/tests/neg/explicit-null-flow.scala b/tests/neg/explicit-null-flow.scala new file mode 100644 index 000000000000..013b56d3f1c2 --- /dev/null +++ b/tests/neg/explicit-null-flow.scala @@ -0,0 +1,182 @@ + +// Flow-sensitive type inference +class Foo { + + def basic() = { + class Bar { + val s: String = ??? + } + + // Basic + val b: Bar|Null = ??? + if (b != null) { + val s = b.s // ok: type of `b` inferred as `Bar` + val s2: Bar = b + } else { + val s = b.s // error: `b` is `Bar|Null` + } + val s = b.s // error: `b` is `Bar|Null` + } + + def notStable() = { + class Bar { + var s: String = ??? + } + + var b2: Bar|Null = ??? + if (b2 != null) { + val s = b.s // error: type of `b2` isn't refined because `b2` is not stable + } + } + + def nested() = { + class Bar2 { + val x: Bar2|Null = ??? + } + + val bar2: Bar2|Null = ??? + if (bar2 != null) { + if (bar2.x != null) { + if (bar2.x.x != null) { + if (bar2.x.x.x != null) { + val b2: Bar2 = bar2.x.x.x + } + val b2: Bar2 = bar2.x.x + val b2err: Bar2 = bar2.x.x.x // error: expected Bar2 but got Bar2|Null + } + val b2: Bar2 = bar2.x + } + val b2: Bar2 = bar2 + } + } + + def ifThenElse() = { + val s: String|Null = ??? + if (s == null) { + } else { + val len: Int = s.length + val len2 = s.length + } + } + + def elseIf() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + if (s1 != null) { + val len = s1.length + val err1 = s2.length // error + val err2 = s3.length // error + } else if (s2 != null) { + val len = s2.length + val err1 = s1.length // error + val err2 = s3.length // error + } else if (s3 != null) { + val len = s3.length + val err1 = s1.length // error + val err2 = s2.length // error + } + + // Accumulation in elseif + if (s1 == null) { + } else if (s2 == null) { + val len = s1.length + } else if (s3 == null) { + val len1 = s1.length + val len2 = s2.length + } else { + val len1 = s1.length + val len2 = s2.length + val len3 = s3.length + } + } + + def commonIdioms() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + + if (s1 == null || s2 == null || s3 == null) { + } else { + val len1: Int = s1.length + val len2: Int = s2.length + val len3: Int = s3.length + } + + if (s1 != null && s2 != null && s3 != null) { + val len1: Int = s1.length + val len2: Int = s2.length + val len3: Int = s3.length + } + } + + def basicNegation() = { + val s1: String|Null = ??? + if (!(s1 != null)) { + val len = s1.length // error + } else { + val len = s1.length + } + + if (!(!(!(!(s1 != null))))) { + val len1 = s1.length + } + } + + def parens() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + if ((((s1 == null))) || s2 == null) { + } else { + val len1 = s1.length + val len2 = s2.length + } + } + + def operatorPrec() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + + if (s1 != null || s2 != null && s3 != null) { + val len = s3.length // error + } + + if (s1 != null && s2 != null || s3 != null) { + val len1 = s1.length // error + val len2 = s2.length // error + val len3 = s3.length // error + } + + if (s1 != null && (s2 != null || s3 != null)) { + val len1 = s1.length + val len2 = s2.length // error + val len3 = s3.length // error + } + } + + def insideCond() = { + val x: String|Null = ??? + if (x != null && x.length > 0) { + val len = x.length + } else { + val len = x.length // error + } + + if (x == null || x.length > 0) { + val len = x.length // error + } else { + val len = x.length + } + + class Rec { + val r: Rec|Null = ??? + } + + val r: Rec|Null = ??? + if (r != null && r.r != null && (r.r.r == null || r.r.r.r == r)) { + val err = r.r.r.r // error + } + } +} + diff --git a/tests/neg/explicit-null-flow2.scala b/tests/neg/explicit-null-flow2.scala new file mode 100644 index 000000000000..7ac243f7fd36 --- /dev/null +++ b/tests/neg/explicit-null-flow2.scala @@ -0,0 +1,18 @@ + +// Test that flow inference can handle blocks. +class Foo { + val x: String|Null = "hello" + if ({val z = 10; {1 + 1 == 2; x != null}}) { + val l = x.length + } + + if ({x != null; true}) { + val l = x.length // error + } + + val x2: String|Null = "world" + if ({{{{1 + 1 == 2; x != null}}}} && x2 != null) { + val l = x.length + val l2 = x2.length + } +} diff --git a/tests/neg/explicit-null-flow3.scala b/tests/neg/explicit-null-flow3.scala new file mode 100644 index 000000000000..59386cf34c07 --- /dev/null +++ b/tests/neg/explicit-null-flow3.scala @@ -0,0 +1,17 @@ + +// Test that flow inference handles `isInstanceOf` checks. +class Foo { + val x: String|Null = "" + if (!x.isInstanceOf[Null]) { + val y = x.length + } + + if (x.isInstanceOf[Null]) { + } else { + val y = x.length + } + + if (x.isInstanceOf[String]) { + val y = x.length // error: we only infer if the type argument is `Null` + } +} diff --git a/tests/neg/explicit-null-flow4.scala b/tests/neg/explicit-null-flow4.scala new file mode 100644 index 000000000000..079f8c97dd1b --- /dev/null +++ b/tests/neg/explicit-null-flow4.scala @@ -0,0 +1,18 @@ + +// Test that flow inference handles `eq/ne` checks. +class Foo { + val x: String|Null = "" + + if (x eq null) { + val y = x.length // error + } else { + val y = x.length + } + + + if (x ne null) { + val y = x.length + } else { + val y = x.length // error + } +} diff --git a/tests/neg/explicit-null-implicits/J.java b/tests/neg/explicit-null-implicits/J.java new file mode 100644 index 000000000000..0f6778071f81 --- /dev/null +++ b/tests/neg/explicit-null-implicits/J.java @@ -0,0 +1,7 @@ + +class J { + Cat getCat() { return null; } +} + +class Cat {} +class Dog {} diff --git a/tests/neg/explicit-null-implicits/S.scala b/tests/neg/explicit-null-implicits/S.scala new file mode 100644 index 000000000000..7d46c1a1cdc5 --- /dev/null +++ b/tests/neg/explicit-null-implicits/S.scala @@ -0,0 +1,16 @@ + +class S { + implicit def cat2Dog(cat: Cat): Dog = ??? + + val j = new J() + // j.getCat() returns a Cat|JavaNull, so we look for implicit conversions + // from Cat to Dog + bark(j.getCat()) + + // We can't find the implicit conversion, because `Null` (as opposed to `JavaNull`) + // isn't stripped. + def getCat2: Cat|Null = null + bark(getCat2) // error + + def bark(dog: Dog) = {} +} diff --git a/tests/neg/explicit-null-interop-javanull.scala b/tests/neg/explicit-null-interop-javanull.scala new file mode 100644 index 000000000000..1a1924016491 --- /dev/null +++ b/tests/neg/explicit-null-interop-javanull.scala @@ -0,0 +1,8 @@ + +// Test that JavaNull can be assigned to Null. +class Foo { + import java.util.ArrayList + val l = new ArrayList[String]() + val s: String = l.get(0) // error: return type is nullable + val s2: String|Null = l.get(0) // ok +} diff --git a/tests/neg/explicit-null-interop-method-src/J.java b/tests/neg/explicit-null-interop-method-src/J.java new file mode 100644 index 000000000000..1b7ea514e4b2 --- /dev/null +++ b/tests/neg/explicit-null-interop-method-src/J.java @@ -0,0 +1,5 @@ + +class J { + String foo(String x) { return null; } + static String fooStatic(String x) { return null; } +} diff --git a/tests/neg/explicit-null-interop-method-src/S.scala b/tests/neg/explicit-null-interop-method-src/S.scala new file mode 100644 index 000000000000..403c86bc4c06 --- /dev/null +++ b/tests/neg/explicit-null-interop-method-src/S.scala @@ -0,0 +1,10 @@ + +class S { + + val j = new J() + j.foo(null) // ok: argument is nullable + val s: String = j.foo("hello") // error: return type is nullable + + J.fooStatic(null) // ok: argument is nullable + val s2: String = J.fooStatic("hello") // error: return type is nullable +} diff --git a/tests/neg/explicit-null-interop-polytypes.scala b/tests/neg/explicit-null-interop-polytypes.scala new file mode 100644 index 000000000000..5718e0fc564d --- /dev/null +++ b/tests/neg/explicit-null-interop-polytypes.scala @@ -0,0 +1,7 @@ +class Foo { + import java.util.ArrayList + // Test that return values in PolyTypes are marked as nullable. + val lstring = new ArrayList[String]() + val res: String = java.util.Collections.max(lstring) // error: missing |Null + val res2: String|Null = java.util.Collections.max(lstring) // ok +} diff --git a/tests/neg/explicit-null-interop-propagate.scala b/tests/neg/explicit-null-interop-propagate.scala new file mode 100644 index 000000000000..c21728fb7395 --- /dev/null +++ b/tests/neg/explicit-null-interop-propagate.scala @@ -0,0 +1,11 @@ + class Foo { + import java.util.ArrayList + + // Test that as we extract return values, we're missing the |JavaNull in the return type. + // i.e. test that the nullability is propagated to nested containers. + val ll = new ArrayList[ArrayList[ArrayList[String]]] + val level1: ArrayList[ArrayList[String]] = ll.get(0) // error + val level2: ArrayList[String] = ll.get(0).get(0) // error + val level3: String = ll.get(0).get(0).get(0) // error + val ok: String = ll.get(0).get(0).get(0) // error +} diff --git a/tests/neg/explicit-null-interop-return.scala b/tests/neg/explicit-null-interop-return.scala new file mode 100644 index 000000000000..677d9528e6fa --- /dev/null +++ b/tests/neg/explicit-null-interop-return.scala @@ -0,0 +1,14 @@ + +// Test that the return type of Java methods as well as the type of Java fields is marked as nullable. +class Foo { + + def foo = { + import java.util.ArrayList + val x = new ArrayList[String]() + val r: String = x.get(0) // error: got String|JavaNull instead of String + + val x2 = new ArrayList[Int]() + val r2: Int = x2.get(0) // error: even though Int is non-nullable in Scala, its counterpart + // (for purposes of generics) in Java (Integer) is. So we're missing |JavaNull + } +} diff --git a/tests/neg/explicit-null-subtype-any.scala b/tests/neg/explicit-null-subtype-any.scala new file mode 100644 index 000000000000..eef5c7296c35 --- /dev/null +++ b/tests/neg/explicit-null-subtype-any.scala @@ -0,0 +1,17 @@ + +// Check that Null is a subtype of Any, but not of AnyRef +class Foo { + + val x1: Any = null + val x2: AnyRef = null // error + val x3: AnyRef|Null = null + val x4: Any|Null = null // Any|Null == Any + + { + def bar(a: Any): Unit = () + val s: String|Null = ??? + bar(s) + val s2: Int|Null = ??? + bar(s2) + } +} diff --git a/tests/neg/i1793.scala b/tests/neg/i1793.scala index ea6d3bcb78c6..e18e7d71f0ff 100644 --- a/tests/neg/i1793.scala +++ b/tests/neg/i1793.scala @@ -1,7 +1,8 @@ object Test { import scala.ref.WeakReference - def unapply[T <: AnyVal](wr: WeakReference[T]): Option[T] = { + def unapply[T <: AnyVal](wr: WeakReference[T]): Option[T] = { // error val x = wr.underlying.get - if (x != null) Some(x) else None // error + if (x != null) Some(x) else None // The failure used to be here, since x had type T <: AnyVal. but now `x: T|Null`, so + // `x != null` is valid. } } diff --git a/tests/neg/i1907.scala b/tests/neg/i1907.scala index 6bc3bb56f7c4..e240b64a9090 100644 --- a/tests/neg/i1907.scala +++ b/tests/neg/i1907.scala @@ -2,6 +2,6 @@ import java.io.File object Test { Some(new File(".")) - .map(_.listFiles).getOrElse(Array.empty) // error: undetermined ClassTag - .map(_.listFiles) + .map(_.listFiles.nn).getOrElse(Array.empty) // error: undetermined ClassTag + .map(_.nn.listFiles) } diff --git a/tests/neg/i2378.scala b/tests/neg/i2378.scala index 1ec5c3c648a8..6eb66bf17e51 100644 --- a/tests/neg/i2378.scala +++ b/tests/neg/i2378.scala @@ -18,7 +18,7 @@ trait Toolbox { class Test(val tb: Toolbox) { import tb._ - implicit val cap: Cap = null + implicit val cap: Cap = ??? def foo(tree: Tree): Int = (tree: Any) match { case tb.Apply(fun, args) => 3 // error, but error message is wrong diff --git a/tests/neg/i2960.scala b/tests/neg/i2960.scala index 3a423612cb19..7914633800b6 100644 --- a/tests/neg/i2960.scala +++ b/tests/neg/i2960.scala @@ -22,7 +22,7 @@ class Tag(val name: String, this } - def apply[U](f: implicit Tag => U)(implicit t: Tag = null): this.type = { + def apply[U](f: implicit Tag => U)(implicit t: Tag|Null = null): this.type = { if(t != null) t.children += this f(this) this diff --git a/tests/neg/inline-case-objects/Macro_1.scala b/tests/neg/inline-case-objects/Macro_1.scala index 95bfeec347bb..a376567d8f48 100644 --- a/tests/neg/inline-case-objects/Macro_1.scala +++ b/tests/neg/inline-case-objects/Macro_1.scala @@ -2,7 +2,7 @@ import scala.quoted._ object Macros { - def impl(foo: Any): Expr[String] = foo.getClass.getCanonicalName.toExpr + def impl(foo: Any): Expr[String] = foo.getClass.getCanonicalName.nn.toExpr } class Bar { diff --git a/tests/neg/opaque-immutable-array.scala b/tests/neg/opaque-immutable-array.scala index 5677b0fa9ebb..ffb1a4ac3fa2 100644 --- a/tests/neg/opaque-immutable-array.scala +++ b/tests/neg/opaque-immutable-array.scala @@ -11,7 +11,8 @@ object ia { // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { - val arr = Arrays.copyOf(ia, ia.length) + import scala.NonNull.ArrayConversions._ + val arr: Array[A] = Arrays.copyOf(ia, ia.length).nn scala.util.Sorting.quickSort(arr) arr } diff --git a/tests/neg/patmat2.scala b/tests/neg/patmat2.scala index 6b7b2c3addbe..e3c940594ad3 100644 --- a/tests/neg/patmat2.scala +++ b/tests/neg/patmat2.scala @@ -4,7 +4,7 @@ import java.lang.IllegalArgumentException object IAE { def unapply(e: Exception): Option[String] = - if (e.isInstanceOf[IllegalArgumentException]) Some(e.getMessage) + if (e.isInstanceOf[IllegalArgumentException]) Some(e.getMessage.nn) else None } diff --git a/tests/neg/t5729.scala b/tests/neg/t5729.scala index 69df11fcbbe0..a6f467798de2 100644 --- a/tests/neg/t5729.scala +++ b/tests/neg/t5729.scala @@ -2,7 +2,7 @@ trait T[X] object Test { def join(in: Seq[T[_]]): Int = ??? def join[S](in: Seq[T[S]]): String = ??? - join(null: Seq[T[_]]) // error: ambiguous + join(??? : Seq[T[_]]) // error: ambiguous } object C { diff --git a/tests/neg/t7278.scala b/tests/neg/t7278.scala index 7b13535f0a60..831df135fa4e 100644 --- a/tests/neg/t7278.scala +++ b/tests/neg/t7278.scala @@ -13,7 +13,7 @@ object Test { def fail1(): Unit = { val b = new B - var x1: EE[A] = null // error: Type argument A does not conform to upper bound EC + var x1: EE[A] = ??? // error: Type argument A does not conform to upper bound EC var x2: EE[B] = new b.E // error: Type argument B does not conform to upper bound EC // x1 = x2 // gives a prior type error: B#E, required: A#E, masked to get at the real thing. } diff --git a/tests/neg/tryPatternMatchEq.scala b/tests/neg/tryPatternMatchEq.scala index d29924d63dd9..17a083818998 100644 --- a/tests/neg/tryPatternMatchEq.scala +++ b/tests/neg/tryPatternMatchEq.scala @@ -4,7 +4,7 @@ import java.lang.IllegalArgumentException object IAE { def unapply(e: Exception): Option[String] = - if (e.isInstanceOf[IllegalArgumentException]) Some(e.getMessage) + if (e.isInstanceOf[IllegalArgumentException]) Some(e.getMessage.nn) else None } diff --git a/tests/pos-java-interop/1576/Test.scala b/tests/pos-java-interop/1576/Test.scala index ea3d06a83005..f87194f141ed 100644 --- a/tests/pos-java-interop/1576/Test.scala +++ b/tests/pos-java-interop/1576/Test.scala @@ -1,5 +1,5 @@ object Test { - val v: TagAnnotation = null + val v: TagAnnotation = ??? println(v.value) // error: value value in class TagAnnotation cannot be accessed as a // member of TagAnnotation(Test.v) from module class Test$. } diff --git a/tests/pos-java-interop/t0695/Test.scala b/tests/pos-java-interop/t0695/Test.scala index 7318867bf7c7..3269f9326a66 100644 --- a/tests/pos-java-interop/t0695/Test.scala +++ b/tests/pos-java-interop/t0695/Test.scala @@ -1,3 +1,3 @@ object Test extends JavaClass[AnyRef] { - var field: InnerClass = null + var field: InnerClass = ??? } diff --git a/tests/pos-java-interop/t1101/S.scala b/tests/pos-java-interop/t1101/S.scala index af7a591e589c..7d7afc5704a0 100644 --- a/tests/pos-java-interop/t1101/S.scala +++ b/tests/pos-java-interop/t1101/S.scala @@ -1 +1 @@ -class S { val x: J.E = null; System.out.println(J.E.E1) } +class S { val x: J.E|Null = null; System.out.println(J.E.E1) } diff --git a/tests/pos-java-interop/t1409/ConcreteImpl.scala b/tests/pos-java-interop/t1409/ConcreteImpl.scala index d427e957e5af..339259594cc6 100644 --- a/tests/pos-java-interop/t1409/ConcreteImpl.scala +++ b/tests/pos-java-interop/t1409/ConcreteImpl.scala @@ -1,3 +1,3 @@ class ConcreteImpl extends AbstractImpl { - def create : OuterInterface.InnerInterface = null + def create : OuterInterface.InnerInterface = ??? } diff --git a/tests/pos-scala2/i3396.scala b/tests/pos-scala2/i3396.scala index 5cf3ccba8980..cdf6cbc38223 100644 --- a/tests/pos-scala2/i3396.scala +++ b/tests/pos-scala2/i3396.scala @@ -5,11 +5,11 @@ object Test { // Negation Tagged: NotTagged[A] is available only if there are no Tagged[A] in scope. trait NotTagged[A] trait NotTaggedLowPrio { - implicit def notTaggedInstance[A]: NotTagged[A] = null + implicit def notTaggedInstance[A]: NotTagged[A] = ??? } object NotTagged extends NotTaggedLowPrio { - implicit def notTaggedAmbiguity1[A](implicit ev: Tagged[A]): NotTagged[A] = null - implicit def notTaggedAmbiguity2[A](implicit ev: Tagged[A]): NotTagged[A] = null + implicit def notTaggedAmbiguity1[A](implicit ev: Tagged[A]): NotTagged[A] = ??? + implicit def notTaggedAmbiguity2[A](implicit ev: Tagged[A]): NotTagged[A] = ??? } @@ -23,7 +23,7 @@ object Test { def main(args: Array[String]): Unit = { - implicit val taggedInt: Tagged[Int] = null + implicit val taggedInt: Tagged[Int] = ??? assert(implicitly[Foo[Int]].value) // fooDefault diff --git a/tests/pos-scala2/naming-resolution/callsite.scala b/tests/pos-scala2/naming-resolution/callsite.scala index 036803a26930..b36580702267 100644 --- a/tests/pos-scala2/naming-resolution/callsite.scala +++ b/tests/pos-scala2/naming-resolution/callsite.scala @@ -6,5 +6,5 @@ package naming.resolution import java.nio.file._ // Imports `Files` object Resolution { - def gimmeFiles: Files = Files.list(Paths.get(".")) + def gimmeFiles: Files|Null = Files.list(Paths.get(".")) } diff --git a/tests/pos-scala2/t3568.scala b/tests/pos-scala2/t3568.scala index 50f0cdb2ebf3..76be56f8b13d 100644 --- a/tests/pos-scala2/t3568.scala +++ b/tests/pos-scala2/t3568.scala @@ -41,6 +41,6 @@ package buffer { class ArrayVec2(val backingSeq: ArrayFloat1) extends GenericSeq[Vec2] with DataArray[Vec2] { def this() = this(new ArrayFloat1) - def apply(i: Int) :Vec2 = null + def apply(i: Int) : Vec2 = ??? } } diff --git a/tests/pos/Meter.scala b/tests/pos/Meter.scala index c32d6a4142f1..272062760065 100644 --- a/tests/pos/Meter.scala +++ b/tests/pos/Meter.scala @@ -7,7 +7,7 @@ package a { class Meter(val underlying: Double) extends AnyVal with _root_.b.Printable { def + (other: Meter): Meter = new Meter(this.underlying + other.underlying) - def / (other: Meter)(implicit dummy: Meter.MeterArg = null): Double = this.underlying / other.underlying + def / (other: Meter)(implicit dummy: Meter.MeterArg = ???): Double = this.underlying / other.underlying def / (factor: Double): Meter = new Meter(this.underlying / factor) def < (other: Meter): Boolean = this.underlying < other.underlying def toFoot: Foot = new Foot(this.underlying * 0.3048) diff --git a/tests/pos/Transactions.scala b/tests/pos/Transactions.scala index dc33e8c377a2..dd5b9482fbea 100644 --- a/tests/pos/Transactions.scala +++ b/tests/pos/Transactions.scala @@ -25,7 +25,7 @@ class Transaction { var id: Long = _ // only for real transactions var head: Transaction = this - var next: Transaction = null + var next: Transaction|Null = null def this(hd: Transaction, tl: Transaction) = { this(); this.head = head; this.next = next } @@ -52,6 +52,8 @@ class Transaction { } trait Transactional { + // TODO(abeln): replace by stdlib function. + implicit def denullify[T](x: T|Null): T = x.asInstanceOf[T] /** create a new snapshot */ def checkPoint(): Unit @@ -60,9 +62,9 @@ trait Transactional { def rollBack(): Unit var readers: Transaction - var writer: Transaction + var writer: Transaction|Null - def currentWriter(): Transaction = null + def currentWriter(): Transaction|Null = null if (writer == null) null else if (writer.status == Transaction.Running) writer else { @@ -83,7 +85,7 @@ trait Transactional { } synchronized { if (thisTrans.status == Transaction.Abortable) throw new AbortException - val w = currentWriter() + val w: Transaction|Null = currentWriter() if (w != null) if (thisTrans.id < w.id) { w.makeAbort(); rollBack(); writer = null } else throw new AbortException @@ -94,7 +96,8 @@ trait Transactional { def setter(thisTrans: Transaction): Unit = { if (writer == thisTrans) return synchronized { - val w = currentWriter() + // TODO(abeln): remove type annotation once type inference works. + val w: Transaction|Null = currentWriter() if (w != null) if (thisTrans.id < w.id) { w.makeAbort(); rollBack() } else throw new AbortException diff --git a/tests/pos/arrays2.scala b/tests/pos/arrays2.scala index c9e5e0bfc7ad..01fd01a9957d 100644 --- a/tests/pos/arrays2.scala +++ b/tests/pos/arrays2.scala @@ -18,13 +18,13 @@ object arrays4 { // #2461 object arrays3 { import scala.collection.JavaConversions._ - def apply[X](xs : X*) : java.util.List[X] = java.util.Arrays.asList(xs: _*) + def apply[X](xs : X*) : java.util.List[X|Null]|Null = java.util.Arrays.asList(xs: _*) - def apply1[X <: String](xs : X*) : java.util.List[X] = java.util.Arrays.asList(xs: _*) - def apply2[X <: AnyVal](xs : X*) : java.util.List[X] = java.util.Arrays.asList(xs: _*) - def apply3(xs : Int*) : java.util.List[Int] = java.util.Arrays.asList(xs: _*) - def apply4(xs : Unit*) : java.util.List[Unit] = java.util.Arrays.asList(xs: _*) - def apply5(xs : Null*) : java.util.List[Null] = java.util.Arrays.asList(xs: _*) - def apply6(xs : Nothing*) : java.util.List[Nothing] = java.util.Arrays.asList(xs: _*) + def apply1[X <: String](xs : X*) : java.util.List[X|Null]|Null = java.util.Arrays.asList(xs: _*) + def apply2[X <: AnyVal](xs : X*) : java.util.List[X|Null]|Null = java.util.Arrays.asList(xs: _*) + def apply3(xs : Int*) : java.util.List[Int|Null]|Null = java.util.Arrays.asList(xs: _*) + def apply4(xs : Unit*) : java.util.List[Unit|Null]|Null = java.util.Arrays.asList(xs: _*) + def apply5(xs : Null*) : java.util.List[Null]|Null = java.util.Arrays.asList(xs: _*) + def apply6(xs : Nothing*) : java.util.List[Nothing|Null]|Null = java.util.Arrays.asList(xs: _*) } diff --git a/tests/pos/attributes.scala b/tests/pos/attributes.scala index 60e00bff7d7c..4f9b19f96956 100644 --- a/tests/pos/attributes.scala +++ b/tests/pos/attributes.scala @@ -56,13 +56,13 @@ object O6 { object myAttrs { class a1 extends scala.annotation.Annotation; class a2(x: Int) extends scala.annotation.Annotation; - class a3(x: a1) extends scala.annotation.Annotation; + class a3(x: a1|Null) extends scala.annotation.Annotation; } class a4(ns: Array[Int]) extends scala.annotation.Annotation; object O7 { class a1 extends scala.annotation.Annotation; class a2(x: Int) extends scala.annotation.Annotation; - class a3(x: a1) extends scala.annotation.Annotation; + class a3(x: a1|Null) extends scala.annotation.Annotation; final val x = new a1; @a1 class C1; diff --git a/tests/pos/capturedVars.scala b/tests/pos/capturedVars.scala index 2cbcf111ae99..21020c930b59 100644 --- a/tests/pos/capturedVars.scala +++ b/tests/pos/capturedVars.scala @@ -7,7 +7,7 @@ class Test { var x: Int = 1 var y: String = "abc" @volatile var vx: Double = 2 - @volatile var vo: Exception = null + @volatile var vo: Exception = ??? var xs: Array[Int] = Array(1, 2, 3) val xs1: Object = xs diff --git a/tests/pos/compound.scala b/tests/pos/compound.scala index 24a936f13e01..4ef457374370 100644 --- a/tests/pos/compound.scala +++ b/tests/pos/compound.scala @@ -3,12 +3,12 @@ abstract class A { type T } abstract class B { val xz: Any } abstract class Test { - var yy: A with B { type T; val xz: T } = null; - var xx: A with B { type T; val xz: T } = null; + var yy: A with B { type T; val xz: T } = ???; + var xx: A with B { type T; val xz: T } = ???; xx = yy; } abstract class Test2 { - var yy: A with B { type T; val xz: T } = null; + var yy: A with B { type T; val xz: T } = ???; val xx: A with B { type T; val xz: T } = yy } diff --git a/tests/pos/devalify.scala b/tests/pos/devalify.scala index b4e4a848cc7f..b85580af6e36 100644 --- a/tests/pos/devalify.scala +++ b/tests/pos/devalify.scala @@ -3,7 +3,7 @@ object Test { trait I { def foo: Any = null } - val s: I = null + val s: I = ??? s.foo } diff --git a/tests/pos/exbound.scala b/tests/pos/exbound.scala index be78abc269de..13a03a22024b 100644 --- a/tests/pos/exbound.scala +++ b/tests/pos/exbound.scala @@ -3,5 +3,5 @@ class A[T <: A[T]] { } object Test { - val x: A[_] = null + val x: A[_] = ??? } diff --git a/tests/pos/existentials-harmful.scala b/tests/pos/existentials-harmful.scala index 91dbd4dfda34..aa0e43096dc7 100644 --- a/tests/pos/existentials-harmful.scala +++ b/tests/pos/existentials-harmful.scala @@ -9,7 +9,7 @@ object ExistentialsConsideredHarmful { trait Tools[A] { def shave(a: A): A } - def tools[A](a: A): Tools[A] = null // dummy + def tools[A](a: A): Tools[A] = ??? // dummy case class TransportBox[A <: Animal](animal: A, tools: Tools[A]) { def label: String = animal.name diff --git a/tests/pos/explicit-null-array.scala b/tests/pos/explicit-null-array.scala new file mode 100644 index 000000000000..f3146c8e8e2b --- /dev/null +++ b/tests/pos/explicit-null-array.scala @@ -0,0 +1,5 @@ +// Test that array contents are non-nullable. +class Foo { + val x: Array[String] = Array("hello") + val s: String = x(0) +} diff --git a/tests/pos/explicit-null-do-not-widen-1/J.java b/tests/pos/explicit-null-do-not-widen-1/J.java new file mode 100644 index 000000000000..c957a1f307b6 --- /dev/null +++ b/tests/pos/explicit-null-do-not-widen-1/J.java @@ -0,0 +1,3 @@ +class J { + String foo() { return "hello"; } +} diff --git a/tests/pos/explicit-null-do-not-widen-1/S.scala b/tests/pos/explicit-null-do-not-widen-1/S.scala new file mode 100644 index 000000000000..0fbca30fac0a --- /dev/null +++ b/tests/pos/explicit-null-do-not-widen-1/S.scala @@ -0,0 +1,7 @@ +class S { + val j = new J() + val x = j.foo() + // Check that the type of `x` is inferred to be `String|Null`. + // i.e. the union isn't collapsed. + val y: String|Null = x +} diff --git a/tests/pos/explicit-null-do-not-widen-2.scala b/tests/pos/explicit-null-do-not-widen-2.scala new file mode 100644 index 000000000000..e35615f7079a --- /dev/null +++ b/tests/pos/explicit-null-do-not-widen-2.scala @@ -0,0 +1,8 @@ + +class S { + def foo[T](x: T): T = x + // Check that the type argument to `foo` is inferred to be + // `String|Null`: i.e. it isn't collapsed. + val x = foo(if (1 == 2) "hello" else null) + val y: String|Null = x +} diff --git a/tests/pos/explicit-null-flow.scala b/tests/pos/explicit-null-flow.scala new file mode 100644 index 000000000000..bbe42a923b4d --- /dev/null +++ b/tests/pos/explicit-null-flow.scala @@ -0,0 +1,160 @@ + +// Flow-sensitive type inference +class Foo { + + def basic() = { + class Bar { + val s: String = ??? + } + + val b: Bar|Null = ??? + if (b != null) { + val s = b.s // ok: type of `b` inferred as `Bar` + val s2: Bar = b + } else { + } + } + + def nestedAndSelection() = { + class Bar2 { + val x: Bar2|Null = ??? + } + + val bar2: Bar2|Null = ??? + if (bar2 != null) { + if (bar2.x != null) { + if (bar2.x.x != null) { + if (bar2.x.x.x != null) { + val b2: Bar2 = bar2.x.x.x + } + val b2: Bar2 = bar2.x.x + } + val b2: Bar2 = bar2.x + } + val b2: Bar2 = bar2 + } + } + + def ifThenElse() = { + val s: String|Null = ??? + if (s == null) { + } else { + val len: Int = s.length + val len2 = s.length + } + } + + + def elseIf() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + if (s1 != null) { + val len = s1.length + } else if (s2 != null) { + val len = s2.length + } else if (s3 != null) { + val len = s3.length + } + + // Accumulation in elseif + if (s1 == null) { + } else if (s2 == null) { + val len = s1.length + } else if (s3 == null) { + val len1 = s1.length + val len2 = s2.length + } else { + val len1 = s1.length + val len2 = s2.length + val len3 = s3.length + } + } + + def commonIdioms() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + + if (s1 == null || s2 == null || s3 == null) { + } else { + val len1: Int = s1.length + val len2: Int = s2.length + val len3: Int = s3.length + } + + if (s1 != null && s2 != null && s3 != null) { + val len1: Int = s1.length + val len2: Int = s2.length + val len3: Int = s3.length + } + } + + def basicNegation() = { + val s1: String|Null = ??? + + if (!(s1 != null)) { + } else { + val len = s1.length + } + + if (!(!(!(!(s1 != null))))) { + val len1 = s1.length + } + } + + def parens() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + + if ((((s1 == null))) || s2 == null) { + } else { + val len1 = s1.length + val len2 = s2.length + } + } + + def operatorPrecedence() = { + val s1: String|Null = ??? + val s2: String|Null = ??? + val s3: String|Null = ??? + + if (s1 != null && (s2 != null || s3 != null)) { + val len1 = s1.length + } + } + + def propInsideCond() = { + val s: String|Null = ??? + if (s != null && s.length > 0) { + val len: Int = s.length + } + + if (s == null || s.length == 0) { + } else { + val len: Int = s.length + } + + class Rec { + val r: Rec|Null = ??? + } + + val r: Rec|Null = ??? + if (r != null && r.r != null && r.r.r != null && (r.r.r.r != null) && r.r.r.r.r != null) { + val r6: Rec|Null = r.r.r.r.r.r + } + + if (r == null || r.r == null || r.r.r == null || (r.r.r.r == null) || r.r.r.r.r == null) { + } else { + val r6: Rec|Null = r.r.r.r.r.r + } + + if (!(r == null) && r.r != null) { + val r3: Rec|Null = r.r.r + } + } + + def interactWithTypeInference() = { + val f: String|Null => Int = (x) => if (x != null) x.length else 0 + } +} diff --git a/tests/pos/explicit-null-flow2.scala b/tests/pos/explicit-null-flow2.scala new file mode 100644 index 000000000000..2391da60b3be --- /dev/null +++ b/tests/pos/explicit-null-flow2.scala @@ -0,0 +1,11 @@ + +class Foo { + + val x: String|Null = ??? + val y: String|Null = ??? + val z: String|Null = ??? + + if ((x != null && z != null) || (y != null && z != null)) { + val z2: String = z + } +} diff --git a/tests/pos/explicit-null-flow3.scala b/tests/pos/explicit-null-flow3.scala new file mode 100644 index 000000000000..21198e09a748 --- /dev/null +++ b/tests/pos/explicit-null-flow3.scala @@ -0,0 +1,9 @@ + +// Test that flow inference can look inside type ascriptions. +// This is useful in combination with inlining (because inlined methods have an ascribed return type). +class Foo { + val x: String|Null = "hello" + if ((x != null): Boolean) { + val y = x.length + } +} diff --git a/tests/pos/explicit-null-flow4.scala b/tests/pos/explicit-null-flow4.scala new file mode 100644 index 000000000000..ef1c8b0c7ab9 --- /dev/null +++ b/tests/pos/explicit-null-flow4.scala @@ -0,0 +1,24 @@ + +// This test is based on tests/pos/rbtree.scala +// and it tests that we can use an inline method to "abstract" a more complicated +// isInstanceOf check, while at the same time getting the flow inference to know +// that `isRedTree(tree) => tree ne null`. +class TreeOps { + abstract class Tree[A, B](val key: A, val value: B) + class RedTree[A, B](override val key: A, override val value: B) extends Tree[A, B](key, value) + + private[this] inline def isRedTree(tree: Tree[_, _]) = (tree ne null) && tree.isInstanceOf[RedTree[_, _]] + + def foo[A, B](tree: Tree[A, B]): Unit = { + if (isRedTree(tree)) { + val key = tree.key + val value = tree.value + } + + if (!isRedTree(tree)) { + } else { + val key = tree.key + val value = tree.value + } + } +} diff --git a/tests/pos/explicit-null-generics/J.java b/tests/pos/explicit-null-generics/J.java new file mode 100644 index 000000000000..b8eab374844b --- /dev/null +++ b/tests/pos/explicit-null-generics/J.java @@ -0,0 +1,9 @@ + +class I {} + +class J { + I foo(T x) { + return new I(); + } + // TODO(abeln): test returning a Scala generic from Java +} diff --git a/tests/pos/explicit-null-generics/S.scala b/tests/pos/explicit-null-generics/S.scala new file mode 100644 index 000000000000..8c33ba3f0368 --- /dev/null +++ b/tests/pos/explicit-null-generics/S.scala @@ -0,0 +1,7 @@ +class ReturnedFromJava[T] {} + +class S { + val j = new J() + // Check that the inside of a Java generic isn't nullified + val i: I[String]|Null = j.foo("hello") +} diff --git a/tests/pos/explicit-null-implicit-arg.scala b/tests/pos/explicit-null-implicit-arg.scala new file mode 100644 index 000000000000..d866cdd67c86 --- /dev/null +++ b/tests/pos/explicit-null-implicit-arg.scala @@ -0,0 +1,17 @@ + +class Foo { + + // Test that the compiler can fill in an implicit argument + // of the form `T|Null`. + def foo(implicit s: String|Null) = s + + { + implicit val arg1: String = "hello" + foo + } + + { + implicit val arg2: Null = null + foo + } +} diff --git a/tests/pos/explicit-null-interop-constructor-src/J.java b/tests/pos/explicit-null-interop-constructor-src/J.java new file mode 100644 index 000000000000..b1590d50023e --- /dev/null +++ b/tests/pos/explicit-null-interop-constructor-src/J.java @@ -0,0 +1,6 @@ +class J { + private String s; + + J(String x) { this.s = x; } + J(String x, String y, String z) {} +} diff --git a/tests/pos/explicit-null-interop-constructor-src/S.scala b/tests/pos/explicit-null-interop-constructor-src/S.scala new file mode 100644 index 000000000000..6cbfea9b57b1 --- /dev/null +++ b/tests/pos/explicit-null-interop-constructor-src/S.scala @@ -0,0 +1,6 @@ + +class S { + val x: J = new J("hello") + val x2: J = new J(null) + val x3: J = new J(null, null, null) +} diff --git a/tests/pos/explicit-null-interop-constructor.scala b/tests/pos/explicit-null-interop-constructor.scala new file mode 100644 index 000000000000..1f631e6efff6 --- /dev/null +++ b/tests/pos/explicit-null-interop-constructor.scala @@ -0,0 +1,7 @@ + +// Test that constructors have a non-nullab.e return type. +class Foo { + val x: java.lang.String = new java.lang.String() + val y: java.util.Date = new java.util.Date() + val v = new java.util.Vector[String](null /*stands for Collection*/) +} diff --git a/tests/pos/explicit-null-interop-javanull-src/J.java b/tests/pos/explicit-null-interop-javanull-src/J.java new file mode 100644 index 000000000000..a85afa17c859 --- /dev/null +++ b/tests/pos/explicit-null-interop-javanull-src/J.java @@ -0,0 +1,8 @@ + +class J1 { + J2 getJ2() { return new J2(); } +} + +class J2 { + J1 getJ1() { return new J1(); } +} diff --git a/tests/pos/explicit-null-interop-javanull-src/S.scala b/tests/pos/explicit-null-interop-javanull-src/S.scala new file mode 100644 index 000000000000..0f5c51a18ccc --- /dev/null +++ b/tests/pos/explicit-null-interop-javanull-src/S.scala @@ -0,0 +1,6 @@ + +// Test that JavaNull is "see through" +class S { + val j: J2 = new J2() + j.getJ1().getJ2().getJ1().getJ2().getJ1().getJ2() +} diff --git a/tests/pos/explicit-null-interop-javanull.scala b/tests/pos/explicit-null-interop-javanull.scala new file mode 100644 index 000000000000..636475166cbf --- /dev/null +++ b/tests/pos/explicit-null-interop-javanull.scala @@ -0,0 +1,10 @@ + +// Tests that the "JavaNull" type added to Java types is "see through" w.r.t member selections. +class Foo { + import java.util.ArrayList + import java.util.Iterator + + // Test that we can select through "|JavaNull" (unsoundly). + val x3 = new ArrayList[ArrayList[ArrayList[String]]]() + val x4: Int = x3.get(0).get(0).get(0).length() +} diff --git a/tests/pos/explicit-null-interop-static/J.java b/tests/pos/explicit-null-interop-static/J.java new file mode 100644 index 000000000000..10965aa9ef4c --- /dev/null +++ b/tests/pos/explicit-null-interop-static/J.java @@ -0,0 +1,4 @@ + +class J { + static int foo(String s) { return 42; } +} diff --git a/tests/pos/explicit-null-interop-static/S.scala b/tests/pos/explicit-null-interop-static/S.scala new file mode 100644 index 000000000000..e54a33cd175b --- /dev/null +++ b/tests/pos/explicit-null-interop-static/S.scala @@ -0,0 +1,6 @@ + +class S { + + J.foo(null) // Java static methods are also nullified + +} diff --git a/tests/pos/explicit-null-interop-valuetypes.scala b/tests/pos/explicit-null-interop-valuetypes.scala new file mode 100644 index 000000000000..595a7de8917a --- /dev/null +++ b/tests/pos/explicit-null-interop-valuetypes.scala @@ -0,0 +1,6 @@ + +// Tests that value (non-reference) types aren't nullified by the Java transform. +class Foo { + val x: java.lang.String = "" + val len: Int = x.length() // type is Int and not Int|JavaNull +} diff --git a/tests/pos/explicit-null-new-array.scala b/tests/pos/explicit-null-new-array.scala new file mode 100644 index 000000000000..a15db12f3340 --- /dev/null +++ b/tests/pos/explicit-null-new-array.scala @@ -0,0 +1,5 @@ + +class S { + val x = new Array[Int](10) + val y = new Array[String](10) +} diff --git a/tests/pos/explicit-null-nn/J.java b/tests/pos/explicit-null-nn/J.java new file mode 100644 index 000000000000..96ac77a528f5 --- /dev/null +++ b/tests/pos/explicit-null-nn/J.java @@ -0,0 +1,4 @@ +class J { + String foo() { return "hello"; } + String[] bar() { return null; } +} diff --git a/tests/pos/explicit-null-nn/S.scala b/tests/pos/explicit-null-nn/S.scala new file mode 100644 index 000000000000..819f080eab0c --- /dev/null +++ b/tests/pos/explicit-null-nn/S.scala @@ -0,0 +1,15 @@ +class S { + val j = new J() + // Test that the `nn` extension method can be used to strip away + // nullability from a type. + val s: String = j.foo.nn + val a: Array[String|Null] = j.bar.nn + + // We can also call .nn on non-nullable types. + val x: String = ??? + val y: String = x.nn + + // And on other Scala code. + val x2: String|Null = null + val y2: String = x2.nn +} diff --git a/tests/pos/explicit-null-or-prototype.scala b/tests/pos/explicit-null-or-prototype.scala new file mode 100644 index 000000000000..60ca9a766f87 --- /dev/null +++ b/tests/pos/explicit-null-or-prototype.scala @@ -0,0 +1,8 @@ + +class Foo { + // Test that prototypes of the form "SomeType | Null" are used in type inference. + // Otherwise, the application of `Array` below would be typed as `Array.apply[String](x)`, + // which doesn't typecheck. + def foo(x: Array[String|Null]|Null) = 0 + foo(Array("hello")) +} diff --git a/tests/pos/explicit-null-pattern-matching.scala b/tests/pos/explicit-null-pattern-matching.scala new file mode 100644 index 000000000000..e1d0da228114 --- /dev/null +++ b/tests/pos/explicit-null-pattern-matching.scala @@ -0,0 +1,33 @@ + +class Foo { + val s: String = ??? + s match { + case s: String => 100 + case _ => 200 // warning: unreachable + } + + sealed trait Animal + case class Dog(name: String) extends Animal + case object Cat extends Animal + + val a: Animal = ??? + a match { + case Dog(name) => 100 + case Cat => 200 + case _ => 300 // warning: unreachable + } + + val a2: Animal|Null = ??? + a2 match { + case Dog(_) => 100 + case Cat => 200 + case _ => 300 // warning: only matches null + } + + val a3: Animal|Null = ??? + a3 match { + case Dog(_) => 100 + case Cat => 200 + case null => 300 // ok + } +} diff --git a/tests/pos/explicit-null-sam-types.scala b/tests/pos/explicit-null-sam-types.scala new file mode 100644 index 000000000000..64bc30c5e774 --- /dev/null +++ b/tests/pos/explicit-null-sam-types.scala @@ -0,0 +1,27 @@ +class Foo { + import java.util.function._ + import java.util.stream._ + + // Assignment context + val p: Predicate[String]|Null = (x) => true + + // Method invocation context + val s: Stream[String] = ??? + s.filter((x) => true) + + // Method overloading context + trait MyFun { + def apply(x: Int): String + } + + def foo(m: MyFun|Null) = {} + def foo(m: Int) = {} + + foo((x) => "hello") + + // Implicit nullary function + { + val f: implicit () => String = "hello" + f + } +} diff --git a/tests/pos/explicit-null-string-whitelist.scala b/tests/pos/explicit-null-string-whitelist.scala new file mode 100644 index 000000000000..256daeac7e3b --- /dev/null +++ b/tests/pos/explicit-null-string-whitelist.scala @@ -0,0 +1,14 @@ +// Test that a few commonly-used functions within `java.lang.String` have been whitelisted +// as returning non-nullable values. +class Foo { + val x1: String = "abc".concat("def") + val x2: String = "abc".replace("a", "b") + val x3: String = "abc".replace("a", "b") + val x4: String = "abc".replace("a", "b") + val x5: Array[String] = "abc".split(" ", 2) + val x6: String = "abc".toLowerCase() + val x7: String = "abc".toUpperCase() + val x8: String = "abc ".trim() + val x9: Array[Char] = "abc".toCharArray() + val x10: String = "abc".substring(1) +} diff --git a/tests/pos/explicit-null-throw-null.scala b/tests/pos/explicit-null-throw-null.scala new file mode 100644 index 000000000000..8abaecdaf5f0 --- /dev/null +++ b/tests/pos/explicit-null-throw-null.scala @@ -0,0 +1,4 @@ + +class Foo { + throw null // Still typecheks: throws NPE +} diff --git a/tests/pos/explicit-null-tostring.scala b/tests/pos/explicit-null-tostring.scala new file mode 100644 index 000000000000..6d4798badfa2 --- /dev/null +++ b/tests/pos/explicit-null-tostring.scala @@ -0,0 +1,8 @@ + +// Check that the return type of toString() isn't nullable. +class Foo { + + val x: java.lang.Integer = 42 + val s: String = x.toString() + +} diff --git a/tests/pos/explicit-null-type-field.scala b/tests/pos/explicit-null-type-field.scala new file mode 100644 index 000000000000..69c5fadef819 --- /dev/null +++ b/tests/pos/explicit-null-type-field.scala @@ -0,0 +1,5 @@ + +class S { + // verify that the special TYPE field is non-nullable + val x: Class[Integer] = java.lang.Integer.TYPE +} diff --git a/tests/pos/explicit-null-union.scala b/tests/pos/explicit-null-union.scala new file mode 100644 index 000000000000..7c1d232af684 --- /dev/null +++ b/tests/pos/explicit-null-union.scala @@ -0,0 +1,4 @@ + +class Foo { + val x: String|Null = null // nullable types can be represented with unions +} diff --git a/tests/pos/explicitOuter.scala b/tests/pos/explicitOuter.scala index 44b441956420..2ca17fb7c545 100644 --- a/tests/pos/explicitOuter.scala +++ b/tests/pos/explicitOuter.scala @@ -51,7 +51,7 @@ class Outer(elem: Int, val next: Outer) { object Test extends App { - val o = new Outer(1, new Outer(2, null)) + val o = new Outer(1, new Outer(2, ???)) val ic = new o.InnerClass(1) println(ic.bar) println(ic.foo) diff --git a/tests/pos/extmethods.scala b/tests/pos/extmethods.scala index fe95a1c79b04..c0f8664247d3 100644 --- a/tests/pos/extmethods.scala +++ b/tests/pos/extmethods.scala @@ -15,7 +15,7 @@ object CollectionStrawMan { implicit class ArrayOps[A](val xs: Array[A]) extends AnyVal { - def elemTag: ClassTag[A] = ClassTag(xs.getClass.getComponentType) + def elemTag: ClassTag[A] = ClassTag(xs.getClass.getComponentType.asInstanceOf[Class[A]]) protected[this] def newBuilder = new ArrayBuffer[A].mapResult(_.toArray(elemTag)) } diff --git a/tests/pos/extractor-types.scala b/tests/pos/extractor-types.scala index 200279be6ffe..377abb5e03ce 100644 --- a/tests/pos/extractor-types.scala +++ b/tests/pos/extractor-types.scala @@ -1,6 +1,6 @@ package p1 { - object Ex { def unapply(p: Any): Option[_ <: Int] = null } - object Foo { val Ex(_) = null } + object Ex { def unapply(p: Any): Option[_ <: Int] = ??? } + object Foo { val Ex(_) = ??? } } // a.scala:2: error: error during expansion of this match (this is a scalac bug). // The underlying error was: type mismatch; @@ -17,8 +17,8 @@ package p2 { } trait Reifiers { def f(): Unit = { - val u2: Other = null - (null: Any) match { case u2.Baz(x) => println(x) } //: u2.Quux) } + val u2: Other = ??? + (??? : Any) match { case u2.Baz(x) => println(x) } //: u2.Quux) } // The underlying error was: type mismatch; // found : Other#Quux // required: u2.Quux diff --git a/tests/pos/extractors.scala b/tests/pos/extractors.scala index 8e5b0e9d2b68..ca6d4c6d648b 100644 --- a/tests/pos/extractors.scala +++ b/tests/pos/extractors.scala @@ -3,7 +3,7 @@ object test { class Tree class Apply(val fun: Tree, val args: List[Tree]) extends Tree - trait DeconstructorCommon[T >: Null <: AnyRef] { + trait DeconstructorCommon[T >: Null <: AnyRef|Null] { var field: T = null def get: this.type = this def isEmpty: Boolean = field eq null @@ -14,14 +14,14 @@ object test { } } - trait ApplyDeconstructor extends DeconstructorCommon[Apply] { + trait ApplyDeconstructor extends DeconstructorCommon[Apply|Null] { def _1: Tree def _2: List[Tree] } object Apply extends ApplyDeconstructor { - def _1: Tree = field.fun - def _2: List[Tree] = field.args + def _1: Tree = field.asInstanceOf[Apply].fun + def _2: List[Tree] = field.asInstanceOf[Apply].args } def assocsFromApply(tree: Tree) = { diff --git a/tests/pos/flow.scala b/tests/pos/flow.scala index 76c0d372c8a3..4ebf03c7102e 100644 --- a/tests/pos/flow.scala +++ b/tests/pos/flow.scala @@ -9,7 +9,7 @@ trait Flow[-In, +Out] extends FlowOps[Out] { class Test { def slowFlow: Unit = { - (null: Flow[String, String]) + (??? : Flow[String, String]) .map(b => b) .map(b => b) .map(b => b) diff --git a/tests/pos/hk.scala b/tests/pos/hk.scala index a8f2aa5971cc..256bc580c2ae 100644 --- a/tests/pos/hk.scala +++ b/tests/pos/hk.scala @@ -23,7 +23,7 @@ object hk0 { object higherKinded { - type Untyped = Null + type Untyped = Nothing class Tree[-T >: Untyped] { type ThisType[-U >: Untyped] <: Tree[U] diff --git a/tests/pos/hklub0.scala b/tests/pos/hklub0.scala index 36cd46332c28..48facaad08b7 100644 --- a/tests/pos/hklub0.scala +++ b/tests/pos/hklub0.scala @@ -1,5 +1,5 @@ object Test { - val a : scala.collection.generic.GenericCompanion[scala.collection.immutable.Seq] = null - val b : scala.collection.generic.GenericCompanion[scala.collection.mutable.Seq] = null + val a: scala.collection.generic.GenericCompanion[scala.collection.immutable.Seq] = ??? + val b: scala.collection.generic.GenericCompanion[scala.collection.mutable.Seq] = ??? List(a, b) // immutable.this.List.apply[scala.collection.generic.GenericCompanion[Seq]](Test.this.a, Test.this.b) } diff --git a/tests/pos/i1044.scala b/tests/pos/i1044.scala index a984dbd67449..97f024b03a8d 100644 --- a/tests/pos/i1044.scala +++ b/tests/pos/i1044.scala @@ -1,3 +1,3 @@ object Test { - val x = ???.getClass.getMethods.head.getParameterTypes.mkString(",") + val x = ???.getClass.getMethods.head.nn.getParameterTypes.nn.mkString(",") } diff --git a/tests/pos/i1045.scala b/tests/pos/i1045.scala index f0cf1df90f8a..57a6a696a126 100644 --- a/tests/pos/i1045.scala +++ b/tests/pos/i1045.scala @@ -16,9 +16,9 @@ object T { class Ident[X >: Null] extends Tree[X] class Apply[X >: Null] extends Tree[X] - val x: Ident[Symbol] | Apply[Symbol] = ??? + val x: Ident[Symbol|Null] | Apply[Symbol|Null] = ??? val y = x.tpe - val z: Symbol = y + val z: Symbol|Null = y } diff --git a/tests/pos/i1352.scala b/tests/pos/i1352.scala index b73ef33fc2cd..83aa2d212982 100644 --- a/tests/pos/i1352.scala +++ b/tests/pos/i1352.scala @@ -5,7 +5,7 @@ object Test { class Foo extends Parent with A class Bar extends Parent with B - (null: Parent) match { + (??? : Parent) match { case (_: A) | (_: B) => /* * This case would incorrectly be reported as an error, diff --git a/tests/pos/i1747.scala b/tests/pos/i1747.scala index 9be62a10a084..6e311898092d 100644 --- a/tests/pos/i1747.scala +++ b/tests/pos/i1747.scala @@ -1,3 +1,3 @@ abstract class Coll[E] extends java.util.Collection[E] { - override def toArray[T](a: Array[T with Object]): Array[T with Object] = ??? + override def toArray[T](a: Array[T with Object|Null]|Null): Array[T with Object|Null]|Null = ??? } diff --git a/tests/pos/i1754.scala b/tests/pos/i1754.scala index bb0da32671d3..37b27e4e96be 100644 --- a/tests/pos/i1754.scala +++ b/tests/pos/i1754.scala @@ -16,9 +16,9 @@ object Test { * @param m The ConcurrentMap to be converted. * @return A Scala mutable ConcurrentMap view of the argument. */ - implicit def mapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = m match { + implicit def mapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]|Null): concurrent.Map[A, B]|Null = m match { case null => null case cmw: ConcurrentMapWrapper[_, _] => cmw.underlying - case _ => new JConcurrentMapWrapper(m) + case cm: juc.ConcurrentMap[A, B] => new JConcurrentMapWrapper(cm) } } diff --git a/tests/pos/i1793.scala b/tests/pos/i1793.scala index fed8a6165699..7c7eac1bfa33 100644 --- a/tests/pos/i1793.scala +++ b/tests/pos/i1793.scala @@ -1,7 +1,8 @@ object Test { import scala.ref.WeakReference def unapply[T <: AnyRef](wr: WeakReference[T]): Option[T] = { - val x = wr.underlying.get - if (x != null) Some(x) else None + val x: T|Null = wr.underlying.get + val x2 = x.asInstanceOf[T] + if (x2 != null) Some(x2) else None } } diff --git a/tests/pos/i2152.scala b/tests/pos/i2152.scala index 2171a487e919..62e2c249672f 100644 --- a/tests/pos/i2152.scala +++ b/tests/pos/i2152.scala @@ -1,6 +1,6 @@ -class Contra[-D](task: AnyRef) +class Contra[-D](task: AnyRef|Null) object Test { - def narrow(task: AnyRef): Contra[task.type] = new Contra(task) + def narrow(task: AnyRef|Null): Contra[task.type] = new Contra(task) def ident[Before](elems: Contra[Before]): Contra[Before] = elems val foo = null ident(narrow(foo)) diff --git a/tests/pos/i2378.scala b/tests/pos/i2378.scala index 26e95207c270..ac9a764f50cb 100644 --- a/tests/pos/i2378.scala +++ b/tests/pos/i2378.scala @@ -17,7 +17,7 @@ trait Toolbox { class Test(val tb: Toolbox) { import tb._ - implicit val cap: Cap = null + implicit val cap: Cap = ??? def foo(tree: Tree): Int = tree match { case Apply(fun, args) => 3 @@ -26,4 +26,4 @@ class Test(val tb: Toolbox) { def bar(tree: tpd.Tree): Int = tree match { case Apply(fun, args) => 3 } -} \ No newline at end of file +} diff --git a/tests/pos/i2554.scala b/tests/pos/i2554.scala index f8a77019c3fd..6ee4973b63d8 100644 --- a/tests/pos/i2554.scala +++ b/tests/pos/i2554.scala @@ -12,7 +12,7 @@ object foo { } object Test { import foo._ - implicit val shape: Shape[_ <: FlatShapeLevel, Int, Int, _] = null + implicit val shape: Shape[_ <: FlatShapeLevel, Int, Int, _] = ??? def hint = Shape.tuple2Shape(shape, shape) val hint2: foo.Shape[foo.FlatShapeLevel, (Int, Int), (Int, Int), _] = hint } diff --git a/tests/pos/i262-null-subtyping.scala b/tests/pos/i262-null-subtyping.scala index 5e57fcca0a8f..ac36f94d5653 100644 --- a/tests/pos/i262-null-subtyping.scala +++ b/tests/pos/i262-null-subtyping.scala @@ -1,16 +1,16 @@ object O { trait Base extends Any { type T } - val a: Base { type T } = null; - val b: Any with Base { type T } = null; + val a: Base { type T } = ???; + val b: Any with Base { type T } = ???; - val c: AnyRef with Base { type T } = null; + val c: AnyRef with Base { type T } = ???; class A class B - val d: A & B = null - val e: A | B = null + val d: A & B = ??? + val e: A | B = ??? - val f: (A & B) { def toString: String } = null - val g: (A | B) { def toString: String } = null + val f: (A & B) { def toString: String } = ??? + val g: (A | B) { def toString: String } = ??? } diff --git a/tests/pos/i2732.scala b/tests/pos/i2732.scala index a37634485409..ba7d131c6b5b 100644 --- a/tests/pos/i2732.scala +++ b/tests/pos/i2732.scala @@ -1,5 +1,5 @@ object Test { val f: java.util.function.Function[_ >: String, _ <: Int] = str => 1 - val i: Int = f("") + val i: Int|Null = f("") } diff --git a/tests/pos/i2749.scala b/tests/pos/i2749.scala index 5d4972421ebb..0e494c209cc2 100644 --- a/tests/pos/i2749.scala +++ b/tests/pos/i2749.scala @@ -8,7 +8,7 @@ object Test { object Test2 { val f: implicit (implicit Int => Char) => Boolean = ??? - implicit val s: String = null + implicit val s: String = ??? implicit val g: implicit Int => implicit String => Char = ??? f : Boolean diff --git a/tests/pos/i2948.scala b/tests/pos/i2948.scala index 0dac0f5284be..61c5eb995f7d 100644 --- a/tests/pos/i2948.scala +++ b/tests/pos/i2948.scala @@ -1,5 +1,5 @@ import scala.collection.mutable.ListBuffer class Foo { - val zipped: ListBuffer[(String, Int)] = null + val zipped: ListBuffer[(String, Int)] = ??? val unzipped: (ListBuffer[String], ListBuffer[Int]) = zipped.unzip } diff --git a/tests/pos/i3273/Test_2.scala b/tests/pos/i3273/Test_2.scala index adc5e362493c..ea1fa844b04a 100644 --- a/tests/pos/i3273/Test_2.scala +++ b/tests/pos/i3273/Test_2.scala @@ -1,3 +1,3 @@ class Test extends Foo_1 { - override def foo(list: java.util.List[_]): Unit = ??? + override def foo(list: java.util.List[_]|Null): Unit = ??? } diff --git a/tests/pos/i342.scala b/tests/pos/i342.scala index bb57bae8e231..7bf7d72a1b87 100644 --- a/tests/pos/i342.scala +++ b/tests/pos/i342.scala @@ -1,6 +1,6 @@ object Test { def test2: Int = { - var ds: String = null + var ds: String = ??? def s = { ds = "abs" ds diff --git a/tests/pos/i3542-2.scala b/tests/pos/i3542-2.scala index 90ece1ea2d78..3ad608dd149e 100644 --- a/tests/pos/i3542-2.scala +++ b/tests/pos/i3542-2.scala @@ -3,16 +3,16 @@ trait ::[H, T] trait Foo[A, R] trait FooLowPrio { - implicit def caseOther[A]: Foo[A, A :: Any] = null + implicit def caseOther[A]: Foo[A, A :: Any] = ??? } object Foo extends FooLowPrio { implicit def caseCons[H, HR, T, TR] (implicit // It's a bit artificial: the by name is not required in this example... t: => Foo[T, TR], h: => Foo[H, HR] - ): Foo[H :: T, TR] = null + ): Foo[H :: T, TR] = ??? - implicit def caseAny: Foo[Any, Any] = null + implicit def caseAny: Foo[Any, Any] = ??? } object Test { diff --git a/tests/pos/i3606.scala b/tests/pos/i3606.scala index 88e5c278ea55..6b24ec8bc1f2 100644 --- a/tests/pos/i3606.scala +++ b/tests/pos/i3606.scala @@ -1,7 +1,7 @@ object Test { def foo: Unit = { - val a: GenericCompanion2[Bar] = null - val b: GenericCompanion2[Baz] = null + val a: GenericCompanion2[Bar] = ??? + val b: GenericCompanion2[Baz] = ??? List(a, b) } } diff --git a/tests/pos/i5067.scala b/tests/pos/i5067.scala new file mode 100644 index 000000000000..6128a9acaa27 --- /dev/null +++ b/tests/pos/i5067.scala @@ -0,0 +1,8 @@ +class Foo { + val Some(_) = ??? + val (_, _, _) = ??? + ??? match { + case Some(_) => () + case (_, _, _) => () + } +} diff --git a/tests/pos/i536.scala b/tests/pos/i536.scala index f2b8f9ce6b28..1ea41d9e9da9 100644 --- a/tests/pos/i536.scala +++ b/tests/pos/i536.scala @@ -6,7 +6,7 @@ object Max { def max[M](x: Coll[_ <: M], cmp: Object): M = ??? val xs: Coll[C] = ??? val m1 = max(xs) - val m2 = max(null) + val m2 = max(???) java.util.Collections.max(null) } diff --git a/tests/pos/i576.scala b/tests/pos/i576.scala index 77b38d742d65..6a091f4b19e7 100644 --- a/tests/pos/i576.scala +++ b/tests/pos/i576.scala @@ -1,7 +1,7 @@ class A object Impl { - def foo()(implicit x: A = null): Int = 2 + def foo()(implicit x: A = ???): Int = 2 def test: Int = { foo()() // ok foo() // did not work before, does now @@ -10,7 +10,7 @@ object Impl { // same with multiple parameters object Impl2 { - def foo()(implicit ev: Int, x: A = null): Int = 2 + def foo()(implicit ev: Int, x: A = ???): Int = 2 def test: Int = { implicit val ii: Int = 1 foo() diff --git a/tests/pos/i801.scala b/tests/pos/i801.scala index 4d8df7f03061..af3563d74953 100644 --- a/tests/pos/i801.scala +++ b/tests/pos/i801.scala @@ -1,9 +1,11 @@ -object T1 { - import java.util.ArrayList, java.util.stream.{Stream => JStream} - new java.util.ArrayList[String]().stream.map(_.toInt).map(_.toString): JStream[String] -} +class Foo { + object T1 { + import java.util.ArrayList, java.util.stream.{Stream => JStream} + new java.util.ArrayList[String]().stream.map(_.nn.toInt).map(_.nn.toString): JStream[String|Null]|Null + } -object T2 { - import java.util._, java.util.stream.{Stream => JStream} - def f: JStream[String] = new java.util.ArrayList[String](Arrays.asList("1", "2")).stream.map(_.toInt).map(_.toString) + object T2 { + import java.util._, java.util.stream.{Stream => JStream} + def f: JStream[String]|Null = new java.util.ArrayList[String](Arrays.asList[String]("1", "2").nn).stream.map(_.nn.toInt).map(_.nn.toString) + } } diff --git a/tests/pos/infer.scala b/tests/pos/infer.scala index 6aeed4049145..4715fda41387 100644 --- a/tests/pos/infer.scala +++ b/tests/pos/infer.scala @@ -5,7 +5,7 @@ object test { case class Cons[a, b <: a](x: a, xs: List[b]) extends List[a] case object Nil extends List[Nothing] def nil[n]: List[n] = Nil - def cons[a](x: a, xs: List[a]): List[a] = null + def cons[a](x: a, xs: List[a]): List[a] = ??? val x: List[Int] = Nil.::(1) val y: List[Int] = nil.::(1) } diff --git a/tests/pos/localmodules.scala b/tests/pos/localmodules.scala index 3e1600842c20..da77150dbf06 100644 --- a/tests/pos/localmodules.scala +++ b/tests/pos/localmodules.scala @@ -16,7 +16,7 @@ object main { def main(args: Array[String]) = { val aa = new a; - val xx: aa.b.c = null; + val xx: aa.b.c = ???; Console.println(aa.bar(xx)); } } diff --git a/tests/pos/opaque-immutable-array.scala b/tests/pos/opaque-immutable-array.scala index 711a8e0a61e8..33feb20f1c24 100644 --- a/tests/pos/opaque-immutable-array.scala +++ b/tests/pos/opaque-immutable-array.scala @@ -13,7 +13,8 @@ object ia { // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { - val arr = Arrays.copyOf(ia, ia.length) + import scala.NonNull.ArrayConversions._ + val arr: Array[A] = Arrays.copyOf(ia, ia.length).nn scala.util.Sorting.quickSort(arr) arr } @@ -44,4 +45,4 @@ object ia { } -lower - 1 } -} \ No newline at end of file +} diff --git a/tests/pos/opaque-nullable.scala b/tests/pos/opaque-nullable.scala index ce32d7c1cfed..a6954d3c71da 100644 --- a/tests/pos/opaque-nullable.scala +++ b/tests/pos/opaque-nullable.scala @@ -1,10 +1,10 @@ object nullable { - opaque type Nullable[A >: Null <: AnyRef] = A + opaque type Nullable[A >: Null <: AnyRef|Null] = A object Nullable { - def apply[A >: Null <: AnyRef](a: A): Nullable[A] = a + def apply[A >: Null <: AnyRef|Null](a: A): Nullable[A] = a - implicit class NullableOps[A >: Null <: AnyRef](na: Nullable[A]) { + implicit class NullableOps[A >: Null <: AnyRef|Null](na: Nullable[A]) { def exists(p: A => Boolean): Boolean = na != null && p(na) def filter(p: A => Boolean): Nullable[A] = @@ -23,4 +23,4 @@ object nullable { Option(na) } } -} \ No newline at end of file +} diff --git a/tests/pos/overloaded_ho_fun.scala b/tests/pos/overloaded_ho_fun.scala index fba1363e60e5..bf7740547944 100644 --- a/tests/pos/overloaded_ho_fun.scala +++ b/tests/pos/overloaded_ho_fun.scala @@ -62,8 +62,8 @@ object SI10194 { trait Z[A] extends Y[A] - (null: Y[Int]).map(x => x.toString) // compiled - (null: Z[Int]).map(x => x.toString) // didn't compile + (??? : Y[Int]).map(x => x.toString) // compiled + (??? : Z[Int]).map(x => x.toString) // didn't compile } // Perform eta-expansion of methods passed as functions to overloaded functions diff --git a/tests/pos/pat_iuli.scala b/tests/pos/pat_iuli.scala index 46356ff58812..bfcec8f99c9b 100644 --- a/tests/pos/pat_iuli.scala +++ b/tests/pos/pat_iuli.scala @@ -9,7 +9,7 @@ trait Ops { self: MyCodes => trait Blox { self: MyCodes => import opcodes._ class Basick { - var foo: Instru = null + var foo: Instru = ??? def bar = foo match { case SWITCH(i) => i diff --git a/tests/pos/protected-static/ScalaClass.scala b/tests/pos/protected-static/ScalaClass.scala index 11108b890d42..165e18ada68b 100644 --- a/tests/pos/protected-static/ScalaClass.scala +++ b/tests/pos/protected-static/ScalaClass.scala @@ -2,5 +2,5 @@ import bippy.JavaClass class Implementor extends JavaClass { import JavaClass.Inner - def getInner: Inner = null + def getInner: Inner = ??? } diff --git a/tests/pos/raw-map/S_2.scala b/tests/pos/raw-map/S_2.scala index d2886fdce9e4..45c3ebe20f20 100644 --- a/tests/pos/raw-map/S_2.scala +++ b/tests/pos/raw-map/S_2.scala @@ -1,6 +1,6 @@ class Foo { def foo: Unit = { - val x: J_1 = null + val x: J_1 = ??? x.setRawType(new java.util.HashMap) } } diff --git a/tests/pos/refinedSubtyping.scala b/tests/pos/refinedSubtyping.scala index e6a972e1c6a8..d65b5ab5bd73 100644 --- a/tests/pos/refinedSubtyping.scala +++ b/tests/pos/refinedSubtyping.scala @@ -64,8 +64,8 @@ class Test4 { abstract class A { type T; val xz: Any } - val yy: A { val xz: T } = null; -// val xx: A { val xz: T } = null; + val yy: A { val xz: T } = ???; +// val xx: A { val xz: T } = ???; val zz: A { val xz: T } = yy; } diff --git a/tests/pos/return_thistype.scala b/tests/pos/return_thistype.scala index c0736c0ad985..df7a070f6c13 100644 --- a/tests/pos/return_thistype.scala +++ b/tests/pos/return_thistype.scala @@ -3,6 +3,6 @@ class As { class A { def foo: A.this.type = bar.asInstanceOf[A.this.type] def foo2: this.type = bar.asInstanceOf[this.type] - def bar: A = null + def bar: A = ??? } } diff --git a/tests/pos/sams.scala b/tests/pos/sams.scala index e18ff81ec805..cdfa01dc2deb 100644 --- a/tests/pos/sams.scala +++ b/tests/pos/sams.scala @@ -77,12 +77,12 @@ class T { object SI9943 { class Foo[T] { - def toMap[K, V](implicit ev: Foo[T] <:< Foo[(K, V)]): Foo[Map[K, V]] = null - def toMap[K](keySelector: T => K): Foo[Map[K, T]] = null + def toMap[K, V](implicit ev: Foo[T] <:< Foo[(K, V)]): Foo[Map[K, V]] = ??? + def toMap[K](keySelector: T => K): Foo[Map[K, T]] = ??? } object Foo { - val f: Foo[Int] = null + val f: Foo[Int] = ??? val m = f.toMap(_ % 2) } } diff --git a/tests/pos/sort.scala b/tests/pos/sort.scala index 97ee3454d660..e7dce45a2a8c 100644 --- a/tests/pos/sort.scala +++ b/tests/pos/sort.scala @@ -1,5 +1,7 @@ object sorting { + import scala.NonNull.ArrayConversions._ + val xs: Array[String] = ??? java.util.Arrays.sort(xs, ???) diff --git a/tests/pos/spec-doubledef-old.scala b/tests/pos/spec-doubledef-old.scala index bde259e4facc..f61d1ad026ab 100644 --- a/tests/pos/spec-doubledef-old.scala +++ b/tests/pos/spec-doubledef-old.scala @@ -18,7 +18,7 @@ abstract class B[T, @specialized(scala.Int) U : Manifest, @specialized(scala.Int val v: V def f(t: T, v2: V): Tuple2[U, V] = { - val m: Array[U] = null + val m: Array[U] = ??? if (m.isEmpty) { (u, v) } else { diff --git a/tests/pos/t0032.scala b/tests/pos/t0032.scala index 727a7d4e992d..a8724dfa19b7 100644 --- a/tests/pos/t0032.scala +++ b/tests/pos/t0032.scala @@ -1,6 +1,6 @@ import java.io.{OutputStream, PrintStream}; -class PromptStream(s: OutputStream) extends PrintStream(s) { +class PromptStream(s: OutputStream|Null) extends PrintStream(s) { override def println() = super.println(); } diff --git a/tests/pos/t0091.scala b/tests/pos/t0091.scala index 414e2c931ab7..1ec750317065 100644 --- a/tests/pos/t0091.scala +++ b/tests/pos/t0091.scala @@ -1,6 +1,6 @@ class Bug { def main(args: Array[String]) = { - var msg: String = null; + var msg: String = ???; val f: PartialFunction[Any, Unit] = { case 42 => msg = "coucou" }; } } diff --git a/tests/pos/t0154.scala b/tests/pos/t0154.scala index 9fb9430676cb..c2bd1eaf046b 100644 --- a/tests/pos/t0154.scala +++ b/tests/pos/t0154.scala @@ -2,7 +2,7 @@ package test trait MyMatchers { val StringMatch = new AnyRef {} trait Something { - (null : AnyRef) match { + (null : AnyRef|Null) match { case (StringMatch) => case _ => } diff --git a/tests/pos/t0872.scala b/tests/pos/t0872.scala index ccaee80521c2..dcae0d338058 100644 --- a/tests/pos/t0872.scala +++ b/tests/pos/t0872.scala @@ -1,7 +1,7 @@ object Main { def main(args : Array[String]): Unit = { val fn = (a : Int, str : String) => "a: " + a + ", str: " + str - implicit def fx[T](f : (T,String) => String): T => String = (x:T) => f(x,null) + implicit def fx[T](f : (T,String) => String): T => String = (x:T) => f(x,???) println(fn(1)) () } diff --git a/tests/pos/t0904.scala b/tests/pos/t0904.scala index 28ad30fc2dde..d66cf721ab6e 100644 --- a/tests/pos/t0904.scala +++ b/tests/pos/t0904.scala @@ -6,8 +6,8 @@ trait A { trait B extends A abstract class Foo { - val a: A = null - val b: B = null + val a: A = ??? + val b: B = ??? a(0) = 1 b(0) = 1 diff --git a/tests/pos/t0905.scala b/tests/pos/t0905.scala index 3800c6e0ba10..ec128e4dc94f 100644 --- a/tests/pos/t0905.scala +++ b/tests/pos/t0905.scala @@ -1,6 +1,6 @@ object Test { trait A[T] - def f(implicit p: A[_]) = null - implicit val x: A[_] = null + def f(implicit p: A[_]) = ??? + implicit val x: A[_] = ??? println(f) } diff --git a/tests/pos/t1001.scala b/tests/pos/t1001.scala index 7a06bfa0e22d..74a86b7bd685 100644 --- a/tests/pos/t1001.scala +++ b/tests/pos/t1001.scala @@ -1,9 +1,11 @@ // was t1001.scala class Foo; +import java.lang.reflect.Constructor + object Overload{ - val foo = classOf[Foo].getConstructors()(0) - foo.getDeclaringClass + val foo: Constructor[_]|Null = classOf[Foo].getConstructors()(0) + foo.asInstanceOf[Constructor[_]].getDeclaringClass } // was t1001.scala diff --git a/tests/pos/t1053.scala b/tests/pos/t1053.scala index 2c5dc1d5a9d8..fe122777fa47 100644 --- a/tests/pos/t1053.scala +++ b/tests/pos/t1053.scala @@ -2,6 +2,6 @@ trait T[A] { trait U { type W = A; val x = 3 } } trait Base { type V } object Test { - val x : (Base { type V = T[this.type] })#V = null + val x: (Base { type V = T[this.type] })#V = ??? val y = new x.U { } } diff --git a/tests/pos/t1107a.scala b/tests/pos/t1107a.scala index 0bf40bb4cc6e..8d77d07d0455 100644 --- a/tests/pos/t1107a.scala +++ b/tests/pos/t1107a.scala @@ -1,6 +1,6 @@ object F { type AnyClass = Class[_] - def tryf[T](ignore: List[AnyClass])(f: => T): Any = { + def tryf[T](ignore: List[AnyClass] | Null)(f: => T): Any = { try { f } catch { diff --git a/tests/pos/t1272.scala b/tests/pos/t1272.scala index 916b783bbb3e..831e6807fbb5 100644 --- a/tests/pos/t1272.scala +++ b/tests/pos/t1272.scala @@ -1,7 +1,7 @@ object ImplicitTest { implicit val i : Int = 10 - implicit def a(implicit i : Int) : Array[Byte] = null - implicit def b[T](implicit i : Int) : Array[T] = null + implicit def a(implicit i : Int) : Array[Byte] = ??? + implicit def b[T](implicit i : Int) : Array[T] = ??? def fn[T](implicit x : T) = 0 diff --git a/tests/pos/t151.scala b/tests/pos/t151.scala index 86667b49f709..86e4a1caf1b2 100644 --- a/tests/pos/t151.scala +++ b/tests/pos/t151.scala @@ -1,6 +1,6 @@ abstract class Foo { type T; def foo(a: T): Int = 0; - val foo: Foo = null; + val foo: Foo = ???; def a: foo.T = a; } diff --git a/tests/pos/t1560.scala b/tests/pos/t1560.scala index dd76392e6679..3be2baad601a 100644 --- a/tests/pos/t1560.scala +++ b/tests/pos/t1560.scala @@ -4,7 +4,7 @@ object Test extends App { def t: T } - def b: Option[C[_]] = null + def b: Option[C[_]] = ??? def c = b match { case Some(b) => b.t diff --git a/tests/pos/t1832.scala b/tests/pos/t1832.scala index c34fe4bfa067..f5b5975a7db1 100644 --- a/tests/pos/t1832.scala +++ b/tests/pos/t1832.scala @@ -4,7 +4,7 @@ trait Cloning { abstract class Star { def *(a: Cloning.this.Foo): Cloning.this.Foo } - implicit def mkStar(i: Int): Star = new Star { def *(a: Foo): Foo = null } + implicit def mkStar(i: Int): Star = new Star { def *(a: Foo): Foo = ??? } val pool = 4 * fn { case ghostSYMBOL: Int => ghostSYMBOL * 2 } } diff --git a/tests/pos/t1896/D0.scala b/tests/pos/t1896/D0.scala index 6b3150d96916..cd72d1bfdc1d 100644 --- a/tests/pos/t1896/D0.scala +++ b/tests/pos/t1896/D0.scala @@ -7,5 +7,5 @@ trait A { } trait B extends A { - def f: Unit = { super.m(null) } + def f: Unit = { super.m(???) } } diff --git a/tests/pos/t2023.scala b/tests/pos/t2023.scala index 21c6fc96a621..5aa6f926b686 100644 --- a/tests/pos/t2023.scala +++ b/tests/pos/t2023.scala @@ -1,7 +1,7 @@ trait C[A] object C { - implicit def ipl[A](implicit from: A => Ordered[A]): C[A] = null + implicit def ipl[A](implicit from: A => Ordered[A]): C[A] = ??? } object P { diff --git a/tests/pos/t2261.scala b/tests/pos/t2261.scala index 06360d50010c..18983ded8aa6 100644 --- a/tests/pos/t2261.scala +++ b/tests/pos/t2261.scala @@ -1,7 +1,7 @@ class Bob[T] object Test { implicit def foo2bar[T](xs: List[T]): Bob[T] = new Bob[T] - var x: Bob[Int] = null + var x: Bob[Int] = ??? x = List(1,2,3) // the problem here was that somehow the type variable that was used to infer the type argument for List.apply // would accumulate several conflicting constraints diff --git a/tests/pos/t229.scala b/tests/pos/t229.scala index 72ddfa74fec9..6a3f92ecd415 100644 --- a/tests/pos/t229.scala +++ b/tests/pos/t229.scala @@ -1,3 +1,3 @@ class Test extends java.util.ArrayList[Object] { - override def add(index: Int, element: java.lang.Object): Unit = {} + override def add(index: Int, element: java.lang.Object|Null): Unit = {} } diff --git a/tests/pos/t245.scala b/tests/pos/t245.scala index 570ac4178d29..f947e4b6a39a 100644 --- a/tests/pos/t245.scala +++ b/tests/pos/t245.scala @@ -6,11 +6,11 @@ object Test { def foo(i: Int): Int = 0 - def fun0 : Value = null - def fun0(i: Int ): Value = null + def fun0 : Value = ??? + def fun0(i: Int ): Value = ??? - def fun1(i: Int ): Value = null - def fun1(l: Long): Value = null + def fun1(i: Int ): Value = ??? + def fun1(l: Long): Value = ??? foo(fun0 ); foo(fun1(new Value)); diff --git a/tests/pos/t247.scala b/tests/pos/t247.scala index fdcafeb2c6cc..71c38e5e6afd 100644 --- a/tests/pos/t247.scala +++ b/tests/pos/t247.scala @@ -20,7 +20,7 @@ class TreeMap[KEY,VALUE](_factory:TreeMapFactory[KEY]) extends Tree[KEY,Tuple2[K val factory = _factory val order = _factory.order; def this(newOrder:Order[KEY]) = this(new TreeMapFactory[KEY](newOrder)); - def get(key:KEY) = null; - def iterator:Iterator[Tuple2[KEY,VALUE]] = null; + def get(key: KEY) = ???; + def iterator: Iterator[Tuple2[KEY,VALUE]] = ???; override def size = super[Tree].size } diff --git a/tests/pos/t2484.scala b/tests/pos/t2484.scala index b822415fd262..1491dc999272 100644 --- a/tests/pos/t2484.scala +++ b/tests/pos/t2484.scala @@ -8,7 +8,7 @@ class Admin extends javax.swing.JApplet { //scala.concurrent.ops.spawn {someFunction ()} jScrollPane.addComponentListener { class nested extends java.awt.event.ComponentAdapter { - override def componentShown (e: java.awt.event.ComponentEvent) = { + override def componentShown (e: java.awt.event.ComponentEvent|Null) = { someFunction (); jScrollPane.removeComponentListener (this) } @@ -26,7 +26,7 @@ class Admin2 extends javax.swing.JApplet { scala.concurrent.Future {jScrollPane.synchronized { def someFunction () = {} //scala.concurrent.ops.spawn {someFunction ()} - jScrollPane.addComponentListener (new java.awt.event.ComponentAdapter {override def componentShown (e: java.awt.event.ComponentEvent) = { + jScrollPane.addComponentListener (new java.awt.event.ComponentAdapter {override def componentShown (e: java.awt.event.ComponentEvent|Null) = { someFunction (); jScrollPane.removeComponentListener (this)}}) }} } diff --git a/tests/pos/t2613.scala b/tests/pos/t2613.scala index 17ebe2d7e9bf..d5b7d7da86a2 100644 --- a/tests/pos/t2613.scala +++ b/tests/pos/t2613.scala @@ -7,5 +7,5 @@ object Test { type M = MyRelation[_ <: Row, _ <: MyRelation[_, _]] - val (x,y): (String, M) = null + val (x,y): (String, M) = ??? } diff --git a/tests/pos/t2619.scala b/tests/pos/t2619.scala index 283d93bf2b97..e6c028ae85c6 100644 --- a/tests/pos/t2619.scala +++ b/tests/pos/t2619.scala @@ -1,11 +1,11 @@ abstract class F { - final def apply(x: Int): AnyRef = null + final def apply(x: Int): AnyRef|Null = null } abstract class AbstractModule { def as: List[AnyRef] def ms: List[AbstractModule] def fs: List[F] = Nil - def rs(x: Int): List[AnyRef] = fs.map(_(x)) + def rs(x: Int): List[AnyRef|Null] = fs.map(_(x)) } abstract class ModuleType1 extends AbstractModule {} abstract class ModuleType2 extends AbstractModule {} diff --git a/tests/pos/t2712-6.scala b/tests/pos/t2712-6.scala index dbba60472cb7..fb32149827ca 100644 --- a/tests/pos/t2712-6.scala +++ b/tests/pos/t2712-6.scala @@ -8,5 +8,5 @@ object Tags { trait Disjunction def meh[M[_], A](ma: M[A]): M[A] = ma - meh(null: Int @@ Disjunction)//.asInstanceOf[Int @@ Disjunction]) + meh(??? : Int @@ Disjunction)//.asInstanceOf[Int @@ Disjunction]) } diff --git a/tests/pos/t2741/2741_1.scala b/tests/pos/t2741/2741_1.scala index d9d04f7ab0b9..9b8a2702bf51 100644 --- a/tests/pos/t2741/2741_1.scala +++ b/tests/pos/t2741/2741_1.scala @@ -3,7 +3,7 @@ trait Partial { } trait MA[M[_]] trait MAs { - val a: MA[Partial#Apply] = null // after compilation, the type is pickled as `MA[ [B] List[B] ]` + val a: MA[Partial#Apply] = ??? // after compilation, the type is pickled as `MA[ [B] List[B] ]` } object Scalaz extends MAs diff --git a/tests/pos/t2913.scala b/tests/pos/t2913.scala index f91ed7b51318..ffb87b1257cc 100644 --- a/tests/pos/t2913.scala +++ b/tests/pos/t2913.scala @@ -47,7 +47,7 @@ object test1 { object Main { def main(args : Array[String]): Unit = { val fn = (a : Int, str : String) => "a: " + a + ", str: " + str - implicit def fx[T](f : (T,String) => String): T => String = (x:T) => f(x,null) + implicit def fx[T](f : (T,String) => String): T => String = (x:T) => f(x,???) println(fn(1)) () } diff --git a/tests/pos/t3528.scala b/tests/pos/t3528.scala index ff49b3e9298b..91bc2dada86d 100644 --- a/tests/pos/t3528.scala +++ b/tests/pos/t3528.scala @@ -4,5 +4,5 @@ class A { // 3528 comments def f2 = List(Set(1,2,3), List(1,2,3)) // 2322 - def f3 = List(null: Range, null: List[Int]) + def f3 = List(??? : Range, ??? : List[Int]) } diff --git a/tests/pos/t359.scala b/tests/pos/t359.scala index 11233c3ba458..544a97aa418f 100644 --- a/tests/pos/t359.scala +++ b/tests/pos/t359.scala @@ -6,8 +6,8 @@ object Bug359 { if (false) { f1(xs) } else { - val a: C = null; - val b: C = null; + val a: C = ???; + val b: C = ???; if (xs.isEmpty) a else b } } @@ -16,13 +16,13 @@ object Bug359 { g { xs => if (false) { - val a: C = null; - val b: C = null; + val a: C = ???; + val b: C = ???; if (xs.isEmpty) a else b } else { f2(xs); } } } - private def g(op: List[C] => C): C = null; + private def g(op: List[C] => C): C = ???; } diff --git a/tests/pos/t3622/Test.scala b/tests/pos/t3622/Test.scala index d18953bbaca4..077dfb7318ab 100644 --- a/tests/pos/t3622/Test.scala +++ b/tests/pos/t3622/Test.scala @@ -1,5 +1,5 @@ package test class Test extends MyAsyncTask { - protected[test] def doInBackground1(args: Array[String]): String = "" + protected[test] def doInBackground1(args: Array[String|Null]|Null): String = "" } diff --git a/tests/pos/t3866.scala b/tests/pos/t3866.scala index f1f64edb9597..ec8d2f7f8ce8 100644 --- a/tests/pos/t3866.scala +++ b/tests/pos/t3866.scala @@ -6,8 +6,8 @@ abstract class ImplicitRepeated { def f[N, R <: List[_]](props: String, elems: T[N, R]*): Unit // alternative b) // the following implicit causes "cannot be applied" errors - implicit def xToRight(r: X): T[Nothing, X] = null - implicit def anyToN[N](x: N): T[N, Nothing] = null + implicit def xToRight(r: X): T[Nothing, X] = ??? + implicit def anyToN[N](x: N): T[N, Nothing] = ??? f("A", 1, 2) // should be implicitly resolved to alternative b) diff --git a/tests/pos/t443.scala b/tests/pos/t443.scala index f1f7ec258642..ea4668af47e3 100644 --- a/tests/pos/t443.scala +++ b/tests/pos/t443.scala @@ -1,13 +1,13 @@ object Test { def lookup(): Option[Tuple2[String, String]] = - ((null: Option[Tuple2[String, String]]) : @unchecked) match { + ((??? : Option[Tuple2[String, String]]) : @unchecked) match { case Some((_, _)) => if (true) - Some((null, null)) + Some((???, ???)) else lookup() match { - case Some(_) => Some(null) + case Some(_) => Some(???) case None => None } } diff --git a/tests/pos/t4579.scala b/tests/pos/t4579.scala index 500ffae40200..2ab202436b98 100644 --- a/tests/pos/t4579.scala +++ b/tests/pos/t4579.scala @@ -97,7 +97,7 @@ object LispCaseClasses extends Lisp { x6: Data, x7: Data, x8: Data, x9: Data): Data = CONS(x0, list(x1, x2, x3, x4, x5, x6, x7, x8, x9)); - var curexp: Data = null + var curexp: Data = ??? var trace: Boolean = false var indent: Int = 0 diff --git a/tests/pos/t4737/S_2.scala b/tests/pos/t4737/S_2.scala index dc89d13168c8..e9d889732daf 100644 --- a/tests/pos/t4737/S_2.scala +++ b/tests/pos/t4737/S_2.scala @@ -3,7 +3,7 @@ package s import j.J_1 class ScalaSubClass extends J_1 { - override def method(javaInnerClass: J_1#JavaInnerClass): Unit = { + override def method(javaInnerClass: J_1#JavaInnerClass|Null): Unit = { println("world") } } diff --git a/tests/pos/t4869.scala b/tests/pos/t4869.scala index f84aa4ed079e..a3900465dac1 100644 --- a/tests/pos/t4869.scala +++ b/tests/pos/t4869.scala @@ -3,6 +3,6 @@ class C[T] class A { - def f[T](x: T): C[_ <: T] = null + def f[T](x: T): C[_ <: T] = ??? def g = List(1d) map f } diff --git a/tests/pos/t5012.scala b/tests/pos/t5012.scala index 84404495c456..2c960c8318cc 100644 --- a/tests/pos/t5012.scala +++ b/tests/pos/t5012.scala @@ -3,7 +3,7 @@ class D { } class C { - def m: D = { + def m: D | Null = { if ("abc".length == 0) { object p // (program point 2) } diff --git a/tests/pos/t5240.scala b/tests/pos/t5240.scala index 065d175f2f80..2af11490ebd9 100644 --- a/tests/pos/t5240.scala +++ b/tests/pos/t5240.scala @@ -6,6 +6,6 @@ package object foo { - var labels: Array[_ <: String] = null + var labels: Array[_ <: String] = ??? } diff --git a/tests/pos/t5359.scala b/tests/pos/t5359.scala index c22b2b1c768c..d319fff31519 100644 --- a/tests/pos/t5359.scala +++ b/tests/pos/t5359.scala @@ -11,7 +11,7 @@ object test { case class S1[F[_]]() extends Step[F] // okay - (null: Step[Option]) match { + (??? : Step[Option]) match { case S1() => } } diff --git a/tests/pos/t5399.scala b/tests/pos/t5399.scala index 0e7cce3c1776..49c3e60daa1b 100644 --- a/tests/pos/t5399.scala +++ b/tests/pos/t5399.scala @@ -18,8 +18,8 @@ class Foo { case class ScopedKey1[T](val foo: Init[T]) extends ScopedKey[T] - val scalaHome: Setting[Option[String]] = null - val scalaVersion: Setting[String] = null + val scalaHome: Setting[Option[String]] = ??? + val scalaVersion: Setting[String] = ??? def testPatternMatch(s: Setting[_]): Unit = { s.key match { diff --git a/tests/pos/t5399a.scala b/tests/pos/t5399a.scala index c40cef4f961b..7939c4d1f64e 100644 --- a/tests/pos/t5399a.scala +++ b/tests/pos/t5399a.scala @@ -8,8 +8,8 @@ class Foo { case class ScopedKey1[T](val foo: Init[T]) extends ScopedKey[T] - val scalaHome: Setting[Option[String]] = null - val scalaVersion: Setting[String] = null + val scalaHome: Setting[Option[String]] = ??? + val scalaVersion: Setting[String] = ??? def testPatternMatch(s: Setting[_]): Unit = { s.key match { diff --git a/tests/pos/t5683.scala b/tests/pos/t5683.scala index 05ab03579274..7113d4b9600a 100644 --- a/tests/pos/t5683.scala +++ b/tests/pos/t5683.scala @@ -4,12 +4,12 @@ object Test { type StringW[T] = W[String, T] trait K[M[_], A, B] - def k[M[_], B](f: Int => M[B]): K[M, Int, B] = null + def k[M[_], B](f: Int => M[B]): K[M, Int, B] = ??? - val okay1: K[StringW,Int,Int] = k{ (y: Int) => null: StringW[Int] } - val okay2 = k[StringW,Int]{ (y: Int) => null: W[String, Int] } + val okay1: K[StringW,Int,Int] = k{ (y: Int) => ??? : StringW[Int] } + val okay2 = k[StringW,Int]{ (y: Int) => ??? : W[String, Int] } - val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + val crash: K[StringW,Int,Int] = k{ (y: Int) => ??? : W[String, Int] } // remove `extends NT[Int]`, and the last line gives an inference error // rather than a crash. @@ -18,6 +18,6 @@ object Test { // argument expression's type is not compatible with formal parameter type; // found : Int => Test.W[String,Int] // required: Int => ?M[?B] - // val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + // val crash: K[StringW,Int,Int] = k{ (y: Int) => ??? : W[String, Int] } // ^ } diff --git a/tests/pos/t5703/Impl.scala b/tests/pos/t5703/Impl.scala index f0120ef0b9a9..405400d01ac4 100644 --- a/tests/pos/t5703/Impl.scala +++ b/tests/pos/t5703/Impl.scala @@ -1,3 +1,3 @@ class Implementation extends Base[Object] { - def func(params: Array[Object]): Unit = {} + def func(params: Array[Object|Null]|Null): Unit = {} } diff --git a/tests/pos/t5720-ownerous.scala b/tests/pos/t5720-ownerous.scala index e171ce9c2a7e..b4251e72ab51 100644 --- a/tests/pos/t5720-ownerous.scala +++ b/tests/pos/t5720-ownerous.scala @@ -31,7 +31,7 @@ class C { def model = Option(m).getOrElse(M("bar")()).copy("baz")("empty") // style points for this version - def modish = ((null: Option[M]) getOrElse new M()()).copy()("empty") + def modish = ((??? : Option[M]) getOrElse new M()()).copy()("empty") // various simplifications are too simple case class N(currentUser: String = "anon") diff --git a/tests/pos/t5727.scala b/tests/pos/t5727.scala index 2c6c0f3056a9..673b05d1a010 100644 --- a/tests/pos/t5727.scala +++ b/tests/pos/t5727.scala @@ -14,8 +14,8 @@ object Test { abstract class Base[+T] { def apply(f: String): Res[T] // 'i' crashes the compiler, similarly if we use currying - //def |[U >: T](a: => Base[U], i: SomeInfo = NoInfo): Base[U] = null - def bar[U >: T](a: => Base[U], i: SomeInfo = NoInfo): Base[U] = null + //def |[U >: T](a: => Base[U], i: SomeInfo = NoInfo): Base[U] = ??? + def bar[U >: T](a: => Base[U], i: SomeInfo = NoInfo): Base[U] = ??? } implicit def fromStringToBase(a: String): Base[String] = new Base[String] { def apply(in: String) = NotRes } @@ -24,7 +24,7 @@ object Test { //def Sample: Base[Any] = ( rep("foo" | "bar") | "sth") def Sample: Base[Any] = ( rep("foo" bar "bar") bar "sth") - def rep[T](p: => Base[T]): Base[T] = null // whatever + def rep[T](p: => Base[T]): Base[T] = ??? // whatever def main(args: Array[String]): Unit = { } diff --git a/tests/pos/t5829.scala b/tests/pos/t5829.scala index 84b450ab31f7..d8489150358f 100644 --- a/tests/pos/t5829.scala +++ b/tests/pos/t5829.scala @@ -10,7 +10,7 @@ trait Universe { } object Test extends App { - val universe: Universe = null + val universe: Universe = ??? import universe._ def select: Select = ??? def ident: Ident = ??? diff --git a/tests/pos/t6942/t6942.scala b/tests/pos/t6942/t6942.scala index 77963d263487..bb5d083c53bc 100644 --- a/tests/pos/t6942/t6942.scala +++ b/tests/pos/t6942/t6942.scala @@ -2,7 +2,7 @@ // its budget should suffice for these simple matches (they do have a large search space) class Test { import foo.Bar // a large enum - def exhaustUnreachabilitysStack_ENUM_STYLE = (null: Bar) match { + def exhaustUnreachabilitysStack_ENUM_STYLE = (??? : Bar) match { case Bar.BULGARIA => case _ => } diff --git a/tests/pos/t6966.scala b/tests/pos/t6966.scala index cd91221a651a..ba9e0f000d00 100644 --- a/tests/pos/t6966.scala +++ b/tests/pos/t6966.scala @@ -2,12 +2,12 @@ import Ordering.{Byte, comparatorToOrdering} trait Format[T] trait InputCache[T] object CacheIvy { - implicit def basicInputCache[I](implicit fmt: Format[I], eqv: Equiv[I]): InputCache[I] = null - implicit def arrEquiv[T](implicit t: Equiv[T]): Equiv[Array[T]] = null - implicit def hNilCache: InputCache[HNil] = null - implicit def ByteArrayFormat: Format[Array[Byte]] = null + implicit def basicInputCache[I](implicit fmt: Format[I], eqv: Equiv[I]): InputCache[I] = ??? + implicit def arrEquiv[T](implicit t: Equiv[T]): Equiv[Array[T]] = ??? + implicit def hNilCache: InputCache[HNil] = ??? + implicit def ByteArrayFormat: Format[Array[Byte]] = ??? type :+:[H, T <: HList] = HCons[H,T] - implicit def hConsCache[H, T <: HList](implicit head: InputCache[H], tail: InputCache[T]): InputCache[H :+: T] = null + implicit def hConsCache[H, T <: HList](implicit head: InputCache[H], tail: InputCache[T]): InputCache[H :+: T] = ??? hConsCache[Array[Byte], HNil] } diff --git a/tests/pos/t7033.scala b/tests/pos/t7033.scala index a4d256673b69..301c78a467b4 100644 --- a/tests/pos/t7033.scala +++ b/tests/pos/t7033.scala @@ -9,7 +9,7 @@ class Wrap { implicit class Y[Y](val a: Y) Y[Int](0) implicit class Z[Z[_]](val a: Z[Wrap.this.Z[Z]]) - Z[List](List(new Z[List](null))) + Z[List](List(new Z[List](???))) } case class X[X](val a: X) diff --git a/tests/pos/t7369.scala b/tests/pos/t7369.scala index 2b43bcaabfe3..feb5cc81cc5b 100644 --- a/tests/pos/t7369.scala +++ b/tests/pos/t7369.scala @@ -1,6 +1,6 @@ object Test { val X, Y = true - (null: Tuple1[Boolean]) match { + (??? : Tuple1[Boolean]) match { case Tuple1(X) => case Tuple1(Y) => // unreachable case _ => @@ -17,7 +17,7 @@ object Test2 { val X: B = True val Y: B = False - (null: Tuple1[B]) match { + (??? : Tuple1[B]) match { case Tuple1(X) => case Tuple1(Y) => // no warning case _ => diff --git a/tests/pos/t7694.scala b/tests/pos/t7694.scala index 9852d5ec79d8..bcaf86a24e65 100644 --- a/tests/pos/t7694.scala +++ b/tests/pos/t7694.scala @@ -9,9 +9,9 @@ object Lub { // use named args transforms to include TypeTree() in the AST before refchecks. def foo(a: L[_, _], b: Any) = 0 - foo(b = 0, a = if (true) (null: L[A, A]) else (null: L[B, B])) + foo(b = 0, a = if (true) (??? : L[A, A]) else (??? : L[B, B])) - (if (true) (null: L[A, A]) else (null: L[B, B])).bar(b = 0, a = 0) + (if (true) (??? : L[A, A]) else (??? : L[B, B])).bar(b = 0, a = 0) } /* diff --git a/tests/pos/t7944.scala b/tests/pos/t7944.scala index 2fe2c5866dbf..fe364cfdee0e 100644 --- a/tests/pos/t7944.scala +++ b/tests/pos/t7944.scala @@ -2,7 +2,7 @@ class M[+A, +B] object Test { implicit class EitherOps[A, B](self: Either[A, B]) { - def disjunction: M[A, B] = null + def disjunction: M[A, B] = ??? } def foo = { diff --git a/tests/pos/t7983.scala b/tests/pos/t7983.scala index fbeb7d3c59a8..b8ac20d2ff1b 100644 --- a/tests/pos/t7983.scala +++ b/tests/pos/t7983.scala @@ -25,7 +25,7 @@ class DivergenceTest { def map1[F, T](f: F)(implicit shape: Shape2[_ <: Flat, F, T]) = ??? - map1(((1, null: Coffees), 1)) - map1(((null: Coffees, 1), 1)) // fails with implicit divergence error in 2.11.0-M6, works under 2.10.3 + map1(((1, ??? : Coffees), 1)) + map1(((??? : Coffees, 1), 1)) // fails with implicit divergence error in 2.11.0-M6, works under 2.10.3 } } diff --git a/tests/pos/t807.scala b/tests/pos/t807.scala index 0eeb92ea2476..79d8177a02b3 100644 --- a/tests/pos/t807.scala +++ b/tests/pos/t807.scala @@ -4,7 +4,7 @@ trait Matcher { type Match <: Link { type Match = Link.this.Self; } } trait HasLinks { - def link(b : Boolean) : Link = null; + def link(b : Boolean) : Link = ???; } } diff --git a/tests/pos/t8177a.scala b/tests/pos/t8177a.scala index 7e2cfb386c3e..4b42ad5551c2 100644 --- a/tests/pos/t8177a.scala +++ b/tests/pos/t8177a.scala @@ -4,6 +4,6 @@ class AA[T](final val x: Thing { type A = T }) { def foo: x.A = ??? } -class B extends AA[Int](null) { +class B extends AA[Int](???) { override def foo: B.this.x.A = super.foo } diff --git a/tests/pos/t8177d.scala b/tests/pos/t8177d.scala index d15a05a359c9..13c1d42b59c2 100644 --- a/tests/pos/t8177d.scala +++ b/tests/pos/t8177d.scala @@ -6,7 +6,7 @@ trait View[AIn] { } object Test { - val view: View[Int] = null + val view: View[Int] = ??? view f2 5 // fails } diff --git a/tests/pos/t8230a.scala b/tests/pos/t8230a.scala index dfbae51eeee2..8d36f4a1fd7f 100644 --- a/tests/pos/t8230a.scala +++ b/tests/pos/t8230a.scala @@ -1,12 +1,12 @@ trait Arr[T] object Arr { - def apply[T](xs: T): Arr[T] = null - def apply(x: Long) : Arr[Long] = null + def apply[T](xs: T): Arr[T] = ??? + def apply(x: Long) : Arr[Long] = ??? } object I { - implicit def arrToTrav[T] (a: Arr[T]) : Traversable[T] = null - implicit def longArrToTrav(a: Arr[Long]): Traversable[Long] = null + implicit def arrToTrav[T] (a: Arr[T]) : Traversable[T] = ??? + implicit def longArrToTrav(a: Arr[Long]): Traversable[Long] = ??? } object Test { diff --git a/tests/pos/tailcall/i321.scala b/tests/pos/tailcall/i321.scala index daa078dd5093..d22f437e3ed8 100644 --- a/tests/pos/tailcall/i321.scala +++ b/tests/pos/tailcall/i321.scala @@ -11,7 +11,7 @@ import scala.annotation.tailrec * For now decision is such - we will abstract for top-level methods, but will not for inner ones. */ -class i321[T >: Null <: AnyRef] { +class i321[T >: Null <: AnyRef|Null] { def go1(f: T => Int): Int = { @tailrec def loop(pending: T): Int = { @@ -21,6 +21,6 @@ class i321[T >: Null <: AnyRef] { loop(null) } - final def go2[U >: Null <: AnyRef](t: i321[U]): Int = t.go2(this) + final def go2[U >: Null <: AnyRef|Null](t: i321[U]): Int = t.go2(this) } diff --git a/tests/pos/tailcall/tailcall.scala b/tests/pos/tailcall/tailcall.scala index faa707e18186..4d790c1a2173 100644 --- a/tests/pos/tailcall/tailcall.scala +++ b/tests/pos/tailcall/tailcall.scala @@ -1,7 +1,7 @@ class tailcall { val shift = 1 final def fact(x: Int, acc: Int = 1): Int = if (x == 0) acc else fact(x - shift, acc * x) - def id[T <: AnyRef](x: T): T = if (x eq null) x else id(x) + def id[T <: AnyRef|Null](x: T): T = if (x eq null) x else id(x) } class TypedApply[T2]{ diff --git a/tests/pos/tcpoly_seq.scala b/tests/pos/tcpoly_seq.scala index 312ba7405818..50a55b14654b 100644 --- a/tests/pos/tcpoly_seq.scala +++ b/tests/pos/tcpoly_seq.scala @@ -67,7 +67,7 @@ trait HOSeq { start = last } else { val last1 = last - last = new HOSeq.this.:: (x, null) // hack: ::'s tail will actually be last + last = new HOSeq.this.:: (x, ???) // hack: ::'s tail will actually be last //last1.tl = last } } diff --git a/tests/pos/tcpoly_seq_typealias.scala b/tests/pos/tcpoly_seq_typealias.scala index 25ec38065668..9d79825f4c1a 100644 --- a/tests/pos/tcpoly_seq_typealias.scala +++ b/tests/pos/tcpoly_seq_typealias.scala @@ -69,7 +69,7 @@ trait HOSeq { start = last } else { val last1 = last - last = new HOSeq.this.:: (x, null) // hack: ::'s tail will actually be last + last = new HOSeq.this.:: (x, ???) // hack: ::'s tail will actually be last //last1.tl = last } } diff --git a/tests/pos/test4.scala b/tests/pos/test4.scala index 4fe65a8f1973..e0161955cf42 100644 --- a/tests/pos/test4.scala +++ b/tests/pos/test4.scala @@ -20,14 +20,14 @@ class O[X]() { trait I[Y] { def foo(x: X, y: Y): E = e; } - val i:I[E] = null; - val j:I[X] = null; + val i: I[E] = ???; + val j: I[X] = ???; } object ooo extends O[C]() { def main = { - val s: S = null; + val s: S = ???; import s._; foo(c,d); ooo.i.foo(c,e); @@ -37,7 +37,7 @@ object ooo extends O[C]() { } class Main() { - val s: S = null; + val s: S = ???; import s._; foo(c,d); ooo.i.foo(c,e); diff --git a/tests/pos/test4a.scala b/tests/pos/test4a.scala index 3efa059b73c2..33af3e121608 100644 --- a/tests/pos/test4a.scala +++ b/tests/pos/test4a.scala @@ -4,7 +4,7 @@ class O[X]() { trait I[Y] { def foo(y: Y): Y = y; } - val j:I[X] = null; + val j: I[X] = ???; } object p extends O[C]() { diff --git a/tests/pos/test4refine.scala b/tests/pos/test4refine.scala index 5e76d10be3a9..da179bae193c 100644 --- a/tests/pos/test4refine.scala +++ b/tests/pos/test4refine.scala @@ -21,15 +21,15 @@ abstract class O() { type Y; def foo(x: X, y: Y): E = e; } - val i:I { type Y = E } = null; - val j:I { type Y = X } = null; + val i: I { type Y = E } = ???; + val j: I { type Y = X } = ???; } object p extends O() { type X = C; def main = { - val s: S = null; + val s: S = ???; import s._; foo(c,d); p.i.foo(c,e); @@ -39,7 +39,7 @@ object p extends O() { } class Main() { - val s: S = null; + val s: S = ???; import s._; foo(c,d); p.i.foo(c,e); diff --git a/tests/pos/test5.scala b/tests/pos/test5.scala index c1947804879c..5906fc89c0fb 100644 --- a/tests/pos/test5.scala +++ b/tests/pos/test5.scala @@ -20,7 +20,7 @@ object test { def val_ix: X = val_ix; } - val i:I[G[P]] = null; + val i: I[G[P]] = ???; // Values with types P and i.X as seen from instances of M def val_mp: P = val_mp; @@ -28,7 +28,7 @@ object test { } class N[Q]() extends M[F[Q]]() { - val j:J[G[Q]] = null; + val j: J[G[Q]] = ???; abstract class J[Y]() extends I[G[Y]]() { // Values with types Y and X as seen from instances of J diff --git a/tests/pos/test5refine.scala b/tests/pos/test5refine.scala index 09ea179da9f7..42bd44262f2e 100644 --- a/tests/pos/test5refine.scala +++ b/tests/pos/test5refine.scala @@ -23,7 +23,7 @@ object test { def val_ix: X = val_ix; } - val i: I { type X = G { type Ig = P } } = null; + val i: I { type X = G { type Ig = P } } = ???; // Values with types P and i.X as seen from instances of M def val_mp: P = val_mp; @@ -33,7 +33,7 @@ object test { abstract class N() extends M() { type Q; type P = F { type If = Q }; - val j:J { type Y = G { type Ig = Q } } = null; + val j: J { type Y = G { type Ig = Q } } = ???; abstract class J() extends I() { type Y; diff --git a/tests/pos/testcast.scala b/tests/pos/testcast.scala index d4184e4f0b45..779f6b0a9593 100644 --- a/tests/pos/testcast.scala +++ b/tests/pos/testcast.scala @@ -7,7 +7,7 @@ class B extends A { } object B { - def view(x: B): B1 = null + def view(x: B): B1 = ??? } class B1 { @@ -15,13 +15,13 @@ class B1 { } object C { - implicit def view(x: A): B1 = null + implicit def view(x: A): B1 = ??? } object Test { import C.view - val b: B = null + val b: B = ??? println(b.bar) } diff --git a/tests/pos/throw-null.scala b/tests/pos/throw-null.scala new file mode 100644 index 000000000000..df6aeb6315ea --- /dev/null +++ b/tests/pos/throw-null.scala @@ -0,0 +1,5 @@ + +object Foo { + val bar: NullPointerException | Null = ??? + throw bar +} diff --git a/tests/pos/ticket2201.scala b/tests/pos/ticket2201.scala index 96b7b4a3d493..f0b48f17b2d2 100644 --- a/tests/pos/ticket2201.scala +++ b/tests/pos/ticket2201.scala @@ -2,7 +2,7 @@ class Test object Test { implicit def view(x : Test): Int = 0 } object Call { - def call(implicit view : Test => Int) = view(null) + def call(implicit view : Test => Int) = view(???) call call } diff --git a/tests/pos/tuplePatDef.scala b/tests/pos/tuplePatDef.scala index 22f8f8e7d6ed..d38f3120169f 100644 --- a/tests/pos/tuplePatDef.scala +++ b/tests/pos/tuplePatDef.scala @@ -1,4 +1,4 @@ object Test { - val (x,y): (String, Int) = null + val (x,y): (String, Int) = ??? } diff --git a/tests/pos/unapplyNeedsMemberType.scala b/tests/pos/unapplyNeedsMemberType.scala index 3a96e189afc6..c36d5fd058e4 100644 --- a/tests/pos/unapplyNeedsMemberType.scala +++ b/tests/pos/unapplyNeedsMemberType.scala @@ -20,6 +20,6 @@ class Join[a] extends Gunk[a] { def unapply_Cons(s: Any) = s match { case App(Cons(x, xs), ys) => Some((x, append(xs, ys))) - case _ => null + case _ => None } } diff --git a/tests/pos/virtpatmat_exist2.scala b/tests/pos/virtpatmat_exist2.scala index f6ebb3ee2f84..7b7a3c9f5bc0 100644 --- a/tests/pos/virtpatmat_exist2.scala +++ b/tests/pos/virtpatmat_exist2.scala @@ -2,14 +2,14 @@ class ParseResult[+T] case class MemoEntry[+T](var r: Either[Nothing,ParseResult[_]]) object Test { - def grow[T]: ParseResult[T] = (null: MemoEntry[T]) match { + def grow[T]: ParseResult[T] = (??? : MemoEntry[T]) match { case MemoEntry(Right(x: ParseResult[_])) => x.asInstanceOf[ParseResult[T]] } // what's the _$1 doing there? // def grow[T >: Nothing <: Any]: ParseResult[T] = { // import OptionMatching._ - // runOrElse[MemoEntry[T], ParseResult[T]]((null: MemoEntry[T]))(((x1: MemoEntry[T]) => + // runOrElse[MemoEntry[T], ParseResult[T]]((??? : MemoEntry[T]))(((x1: MemoEntry[T]) => // (MemoEntry.unapply[T](x1).flatMap[ParseResult[T]](((x4: Either[Nothing,ParseResult[_]]) => // guard[Right[Nothing,ParseResult[_]]](x4.isInstanceOf[Right[Nothing,ParseResult[_]]], x4.asInstanceOf[Right[Nothing,ParseResult[_]]]).flatMap[ParseResult[T]](((cp3: Right[Nothing,ParseResult[_]]) => // scala.Right.unapply[Nothing, ParseResult[_]](cp3).flatMap[ParseResult[T]](((x5: ParseResult[_]) => diff --git a/tests/pos/virtpatmat_exist3.scala b/tests/pos/virtpatmat_exist3.scala index 6a6d428b1a09..d96d512f04d1 100644 --- a/tests/pos/virtpatmat_exist3.scala +++ b/tests/pos/virtpatmat_exist3.scala @@ -1,5 +1,5 @@ class ReferenceQueue[T] { - def wrapper(jref: ReferenceQueue[_]): ReferenceQueue[T] = + def wrapper(jref: ReferenceQueue[_]|Null): ReferenceQueue[T]|Null = jref match { case null => null } diff --git a/tests/pos/virtpatmat_exist4.scala b/tests/pos/virtpatmat_exist4.scala index 728006276350..1e9972ce953e 100644 --- a/tests/pos/virtpatmat_exist4.scala +++ b/tests/pos/virtpatmat_exist4.scala @@ -17,7 +17,7 @@ trait MemberHandlers { } object Test { - var intp: IMain with MemberHandlers = null + var intp: IMain with MemberHandlers = ??? val handlers = intp.handlers handlers.filterNot(_.importedSymbols.isEmpty).zipWithIndex foreach { diff --git a/tests/pos/virtpatmat_gadt_array.scala b/tests/pos/virtpatmat_gadt_array.scala index 02dbad68d9d0..dcd3f02bb8fe 100644 --- a/tests/pos/virtpatmat_gadt_array.scala +++ b/tests/pos/virtpatmat_gadt_array.scala @@ -1,6 +1,6 @@ import scala.collection.mutable._ object Test { - def genericArrayOps[T](xs: Array[T]): ArrayOps[T] = xs match { + def genericArrayOps[T](xs: Array[T]|Null): ArrayOps[T]|Null = xs match { case x: Array[AnyRef] => refArrayOps[AnyRef](x).asInstanceOf[ArrayOps[T]] case null => null } diff --git a/tests/pos/wildcardBoundInference.scala b/tests/pos/wildcardBoundInference.scala index 65553ed93619..207acae81705 100644 --- a/tests/pos/wildcardBoundInference.scala +++ b/tests/pos/wildcardBoundInference.scala @@ -7,7 +7,7 @@ public class Exist { } */ class Exist[T <: String] { - def foo: Exist[_] = null + def foo: Exist[_] = ??? } /* @@ -18,7 +18,7 @@ public class ExistF> { */ class ExistF[T <: ExistF[T]] { - def foo: ExistF[_] = null + def foo: ExistF[_] = ??? } /* @@ -29,7 +29,7 @@ public class ExistIndir { */ class ExistIndir[T <: String, U <: T] { - def foo: ExistIndir[_, _] = null + def foo: ExistIndir[_, _] = ??? } class Test { diff --git a/tests/pos/z1730.scala b/tests/pos/z1730.scala index 574b9bbd03d3..896e2942180d 100644 --- a/tests/pos/z1730.scala +++ b/tests/pos/z1730.scala @@ -6,7 +6,7 @@ class X[R] { } class Boo { - implicit def toX[R](v: R) : X[R] = null + implicit def toX[R](v: R) : X[R] = ??? def goo2: Unit = { 3.xx(34) } diff --git a/tests/run/2772.scala b/tests/run/2772.scala index fcde4c363b35..8268105977f1 100644 --- a/tests/run/2772.scala +++ b/tests/run/2772.scala @@ -2,7 +2,7 @@ import java.io.OutputStream object Test { def main(args: Array[String]): Unit = { - val oldErr = System.err + val oldErr = System.err.nn System.setErr(null) // setOut(null) confuses the testing framework... val a = () => foo(oldErr) a() diff --git a/tests/run/Course-2002-09.scala b/tests/run/Course-2002-09.scala index 0aa6209a2951..a544426e79f8 100644 --- a/tests/run/Course-2002-09.scala +++ b/tests/run/Course-2002-09.scala @@ -94,7 +94,7 @@ class Probe(name: String, q: Quantity) extends Constraint { class Quantity() { private var value: Option[Double] = None; private var constraints: List[Constraint] = List(); - private var informant: Constraint = null; + private var informant: Constraint|Null = null; def getValue: Option[Double] = value; diff --git a/tests/run/Course-2002-13.scala b/tests/run/Course-2002-13.scala index 774695b6c406..9d46d4243dc5 100644 --- a/tests/run/Course-2002-13.scala +++ b/tests/run/Course-2002-13.scala @@ -190,7 +190,7 @@ class Parser(s: String) { def term: Term = { val ch = token.charAt(0); if ('A' <= ch && ch <= 'Z') { val a = token; token = it.next; Var(a) } - else if (it.isDelimiter(ch)) { syntaxError("term expected"); null } + else if (it.isDelimiter(ch)) { syntaxError("term expected"); ??? } else constructor } diff --git a/tests/run/Meter.scala b/tests/run/Meter.scala index d070b7668724..4d041439b545 100644 --- a/tests/run/Meter.scala +++ b/tests/run/Meter.scala @@ -7,7 +7,7 @@ package a { class Meter(val underlying: Double) extends AnyVal with _root_.b.Printable { def + (other: Meter): Meter = new Meter(this.underlying + other.underlying) - def / (other: Meter)(implicit dummy: Meter.MeterArg = null): Double = this.underlying / other.underlying + def / (other: Meter)(implicit dummy: Meter.MeterArg|Null = null): Double = this.underlying / other.underlying def / (factor: Double): Meter = new Meter(this.underlying / factor) def < (other: Meter): Boolean = this.underlying < other.underlying def toFoot: Foot = new Foot(this.underlying * 0.3048) diff --git a/tests/run/MeterCaseClass.scala b/tests/run/MeterCaseClass.scala index 1dcd9f34463a..d2112580d0ab 100644 --- a/tests/run/MeterCaseClass.scala +++ b/tests/run/MeterCaseClass.scala @@ -7,7 +7,7 @@ package a { case class Meter(underlying: Double) extends AnyVal with _root_.b.Printable { def + (other: Meter): Meter = new Meter(this.underlying + other.underlying) - def / (other: Meter)(implicit dummy: Meter.MeterArg = null): Double = this.underlying / other.underlying + def / (other: Meter)(implicit dummy: Meter.MeterArg|Null = null): Double = this.underlying / other.underlying def / (factor: Double): Meter = new Meter(this.underlying / factor) def < (other: Meter): Boolean = this.underlying < other.underlying def toFoot: Foot = new Foot(this.underlying * 0.3048) diff --git a/tests/run/array-charSeq.scala b/tests/run/array-charSeq.scala index 64055c6406ba..d551dd80b906 100644 --- a/tests/run/array-charSeq.scala +++ b/tests/run/array-charSeq.scala @@ -12,17 +12,17 @@ object Test { } 0 until chars.length foreach { i => - println("sub(%s, %s) == '%s'".format(i, chars.length, chars.subSequence(i, chars.length))) - println("sub(%s, %s) == '%s'".format(0, i, chars.subSequence(0, i))) + println("sub(%s, %s) == '%s'".format(i, chars.length, chars.subSequence(i, chars.length).nn)) + println("sub(%s, %s) == '%s'".format(0, i, chars.subSequence(0, i).nn)) } if (chars.length >= 2) - check(chars.subSequence(1, chars.length - 1)) + check(chars.subSequence(1, chars.length - 1).nn) } def main(args: Array[String]): Unit = { while (xs.length > 0) { check(xs) - xs = xs.subSequence(0, xs.length - 1) + xs = xs.subSequence(0, xs.length - 1).nn } } } diff --git a/tests/run/bigDecimalCache.scala b/tests/run/bigDecimalCache.scala index c0c709a50f7d..d7748d888edf 100644 --- a/tests/run/bigDecimalCache.scala +++ b/tests/run/bigDecimalCache.scala @@ -1,7 +1,7 @@ object Test { def main(args: Array[String]): Unit = { val bd5a = BigDecimal(5) - val mc = java.math.MathContext.DECIMAL32 + val mc = java.math.MathContext.DECIMAL32.nn val bd5b = BigDecimal(5,mc) assert(bd5b.mc == mc) diff --git a/tests/run/concat-two-strings.scala b/tests/run/concat-two-strings.scala index c8881aa14627..4731b3f736bd 100644 --- a/tests/run/concat-two-strings.scala +++ b/tests/run/concat-two-strings.scala @@ -4,7 +4,7 @@ object Test { def f1(x: AnyRef) = "" + x def f2(x: Int) = "" + x - def f3(x: Array[Char]) = "" + x + def f3(x: Array[Char]|Null) = "" + x def f4(x: List[Int]) = "" + x def f5(x: Any) = "" + x def f6(x: AnyVal) = "" + x diff --git a/tests/run/delambdafy-dependent-on-param-subst-2.scala b/tests/run/delambdafy-dependent-on-param-subst-2.scala index 4f7b2dc6e7b4..e89e59439ce6 100644 --- a/tests/run/delambdafy-dependent-on-param-subst-2.scala +++ b/tests/run/delambdafy-dependent-on-param-subst-2.scala @@ -13,8 +13,8 @@ object Test { // (new O: M[Null]).m(null) // Okay ((a: A) => { - class N extends M[a.C] { def m(x: a.C) = true } + class N extends M[a.C|Null] { def m(x: a.C|Null) = true } new N: M[Null] - }).apply(a).m(null) // NPE, missing bridge + }).apply(a).m(null) // This used to be incorrectly marked as throwing an NPE. } } diff --git a/tests/run/enum-approx.scala b/tests/run/enum-approx.scala index f18af97df40a..b6a06165ff21 100644 --- a/tests/run/enum-approx.scala +++ b/tests/run/enum-approx.scala @@ -1,5 +1,5 @@ enum Fun[-T, +U >: Null] { - def f: T => U = this match { + def f: (T => U)|Null = this match { case Identity(g) => g case ConstNull => (_ => null) case ConstNullClass() => (_ => null) @@ -14,8 +14,8 @@ enum Fun[-T, +U >: Null] { object Test { def main(args: Array[String]) = { - val x: Null = Fun.ConstNull.f("abc") - val y: Null = Fun.ConstNullClass().f("abc") + val x: Null = Fun.ConstNull.f.nn("abc") + val y: Null = Fun.ConstNullClass().f.nn("abc") assert(Fun.ConstNullSimple.f == null) } } diff --git a/tests/run/erased-frameless.scala b/tests/run/erased-frameless.scala index 5c18ef378d5f..c45ec951d52d 100644 --- a/tests/run/erased-frameless.scala +++ b/tests/run/erased-frameless.scala @@ -71,17 +71,17 @@ trait Exists[T, K, V] object Exists { implicit def derive[T, H <: HList, K, V](implicit g: LabelledGeneric[T] { type Repr = H }, s: Selector[H, K, V]): Exists[T, K, V] = { println("Exists.derive") - null + new Exists[T, K, V] {} } implicit def caseFound[T <: HList, K <: String, V]: Selector[R[K, V] :: T, K, V] = { println("Selector.caseFound") - null + new Selector[R[K, V] :: T, K, V] {} } implicit def caseRecur[H, T <: HList, K <: String, V](implicit i: Selector[T, K, V]): Selector[H :: T, K, V] = { println("Selector.caseRecur") - null + new Selector[H :: T, K, V] {} } } @@ -96,7 +96,7 @@ object X4 { type Repr = R["a", A] :: R["b", B] :: R["c", C] :: R["d", D] :: HNil } = { println("X4.x4Repr") - null + new LabelledGeneric[X4[A, B, C, D]] { type Repr = R["a", A] :: R["b", B] :: R["c", C] :: R["d", D] :: HNil } } } diff --git a/tests/run/exceptions-2.scala b/tests/run/exceptions-2.scala index 8d755c380969..01de752b23db 100644 --- a/tests/run/exceptions-2.scala +++ b/tests/run/exceptions-2.scala @@ -91,8 +91,8 @@ object Test { } try { - val a: Leaf = null; - println(a.x); + val a: Leaf|Null = null; + println(a.nn.x); } catch { case Leaf(a) => Console.println(a); case _: NullPointerException => Console.println("Exception occurred"); @@ -101,8 +101,8 @@ object Test { def method3: Unit = try { try { - val a: Leaf = null; - println(a.x); + val a: Leaf|Null = null; + println(a.nn.x); } catch { case Leaf(a) => Console.println(a); } diff --git a/tests/run/exceptions-nest.scala b/tests/run/exceptions-nest.scala index 87586485c0c9..62255ac05f33 100644 --- a/tests/run/exceptions-nest.scala +++ b/tests/run/exceptions-nest.scala @@ -83,7 +83,7 @@ object Test extends dotty.runtime.LegacyApp { var x = 1 try { x = 2 - (null: String).toString + (null: String|Null).nn.toString } catch { case e: NullPointerException => throw e diff --git a/tests/run/explicit-null-arrays.scala b/tests/run/explicit-null-arrays.scala new file mode 100644 index 000000000000..05b115c350bb --- /dev/null +++ b/tests/run/explicit-null-arrays.scala @@ -0,0 +1,20 @@ + +object Test { + def sort[T <: AnyRef : Ordering](a: Array[T]): Array[T] = { + import java.util.Arrays + import scala.NonNull.ArrayConversions._ + val a2: Array[T] = Arrays.copyOf(a, a.length).nn + scala.util.Sorting.quickSort(a2) + a2 + } + + def main(args: Array[String]): Unit = { + val a: Array[Integer] = Array(3, 2, 4, 1) + val a2 = sort(a) + assert(a2(0) == 1) + assert(a2(1) == 2) + assert(a2(2) == 3) + assert(a2(3) == 4) + } +} + diff --git a/tests/run/explicit-null-byname-varargs.scala b/tests/run/explicit-null-byname-varargs.scala new file mode 100644 index 000000000000..13023d0dd6f3 --- /dev/null +++ b/tests/run/explicit-null-byname-varargs.scala @@ -0,0 +1,11 @@ + +// Test that by-name varargs don't crash at runtime. +object Test { + def foo(x: => Any*): Unit = x + + def main(args: Array[String]): Unit = { + foo("hello") + foo(42) + foo("a", "b", 200) + } +} diff --git a/tests/run/explicit-null-nn.scala b/tests/run/explicit-null-nn.scala new file mode 100644 index 000000000000..30ce120e8342 --- /dev/null +++ b/tests/run/explicit-null-nn.scala @@ -0,0 +1,12 @@ +// Check that calling `.nn` on a null value throws a NPE. +object Test { + def len(x: Array[String]|Null): Unit = x.nn.length + def load(x: Array[String]|Null): Unit = x.nn(0) + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]): Unit = { + check(len(null)) + check(load(null)) + } +} diff --git a/tests/run/explicit-null-subtype-any.scala b/tests/run/explicit-null-subtype-any.scala new file mode 100644 index 000000000000..31a5bb66f092 --- /dev/null +++ b/tests/run/explicit-null-subtype-any.scala @@ -0,0 +1,28 @@ + +object Test { + + def main(args: Array[String]): Unit = { + assert(null.eq(null)) + assert(!null.ne(null)) + + assert(!null.eq("hello")) + assert(null.ne("hello")) + + assert(!null.eq(4)) + assert(null.ne(4)) + + assert(!"hello".eq(null)) + assert("hello".ne(null)) + + assert(!4.eq(null)) + assert(4.ne(null)) + + val x: String|Null = null + assert(x.eq(null)) + assert(!x.ne(null)) + + val x2: AnyRef|Null = "world" + assert(!x2.eq(null)) + assert(x2.ne(null)) + } +} diff --git a/tests/run/forwarder.scala b/tests/run/forwarder.scala index 540acb76a58b..83e9638050d1 100644 --- a/tests/run/forwarder.scala +++ b/tests/run/forwarder.scala @@ -14,7 +14,7 @@ object Test { def main(args: Array[String]): Unit = { println( classOf[Foo].getMethods - .filter(m => (m.getModifiers & java.lang.reflect.Modifier.STATIC) != 0) + .filter(m => (m.nn.getModifiers & java.lang.reflect.Modifier.STATIC) != 0) .mkString("\n")) } } diff --git a/tests/run/generic/Enum.scala b/tests/run/generic/Enum.scala index ce64ac054933..582072f748ef 100644 --- a/tests/run/generic/Enum.scala +++ b/tests/run/generic/Enum.scala @@ -7,7 +7,7 @@ trait Enum { object runtime { class EnumValues[E <: Enum] { private[this] var myMap: Map[Int, E] = Map() - private[this] var fromNameCache: Map[String, E] = null + private[this] var fromNameCache: Map[String, E]|Null = null def register(v: E) = { require(!myMap.contains(v.enumTag)) @@ -18,8 +18,8 @@ object runtime { def fromInt: Map[Int, E] = myMap def fromName: Map[String, E] = { if (fromNameCache == null) fromNameCache = myMap.values.map(v => v.toString -> v).toMap - fromNameCache + fromNameCache.nn } def values: Iterable[E] = myMap.values } -} \ No newline at end of file +} diff --git a/tests/run/generic/Serialization.scala b/tests/run/generic/Serialization.scala index a82d6bc7a100..56f0e2b8bfe2 100644 --- a/tests/run/generic/Serialization.scala +++ b/tests/run/generic/Serialization.scala @@ -46,7 +46,7 @@ object Serialization { implicit val StringSerializable: Serializable[String] = new Serializable[String] { def write(x: String, out: DataOutputStream) = out.writeUTF(x) - def read(in: DataInputStream) = in.readUTF() + def read(in: DataInputStream) = in.readUTF().nn } def RecSerializable[T, U](implicit diff --git a/tests/run/gestalt-optional-inline/Test_2.scala b/tests/run/gestalt-optional-inline/Test_2.scala index 039091974350..239c6a8f85fc 100644 --- a/tests/run/gestalt-optional-inline/Test_2.scala +++ b/tests/run/gestalt-optional-inline/Test_2.scala @@ -16,18 +16,18 @@ object Test { } def getOrElseTest(): Unit = { - val opt = new Optional[String]("hello") + val opt = new Optional[String|Null]("hello") assert(opt.getOrElse("world") == "hello") - val opt2 = new Optional[String](null) + val opt2 = new Optional[String|Null](null) assert(opt2.getOrElse("hello") == "hello") } def mapTest(): Unit = { - val opt = new Optional[String]("hello") + val opt = new Optional[String|Null]("hello") assert(opt.map(_ + " world") == new Optional("hello world")) - val opt2 = new Optional[String](null) + val opt2 = new Optional[String|Null](null) assert(opt2.map(_ + " world") == new Optional(null)) } @@ -64,7 +64,7 @@ object Test { } def `owner chain corruptionTest`(): Unit = { - def foo(x: => Optional[C]) = x + def foo(x: => Optional[C|Null]) = x foo({ val y = new Optional(null); y }).getOrElse(new C) } @@ -74,7 +74,7 @@ object Test { } def `the final thingTest`(): Unit = { - def foo(f: => C): C = f + def foo(f: => C|Null): C|Null = f val x1 = new Optional(new C) val x2 = x1.map(x => foo({ val y = x; y })) } diff --git a/tests/run/gestalt-optional-staging/Test_2.scala b/tests/run/gestalt-optional-staging/Test_2.scala index 039091974350..239c6a8f85fc 100644 --- a/tests/run/gestalt-optional-staging/Test_2.scala +++ b/tests/run/gestalt-optional-staging/Test_2.scala @@ -16,18 +16,18 @@ object Test { } def getOrElseTest(): Unit = { - val opt = new Optional[String]("hello") + val opt = new Optional[String|Null]("hello") assert(opt.getOrElse("world") == "hello") - val opt2 = new Optional[String](null) + val opt2 = new Optional[String|Null](null) assert(opt2.getOrElse("hello") == "hello") } def mapTest(): Unit = { - val opt = new Optional[String]("hello") + val opt = new Optional[String|Null]("hello") assert(opt.map(_ + " world") == new Optional("hello world")) - val opt2 = new Optional[String](null) + val opt2 = new Optional[String|Null](null) assert(opt2.map(_ + " world") == new Optional(null)) } @@ -64,7 +64,7 @@ object Test { } def `owner chain corruptionTest`(): Unit = { - def foo(x: => Optional[C]) = x + def foo(x: => Optional[C|Null]) = x foo({ val y = new Optional(null); y }).getOrElse(new C) } @@ -74,7 +74,7 @@ object Test { } def `the final thingTest`(): Unit = { - def foo(f: => C): C = f + def foo(f: => C|Null): C|Null = f val x1 = new Optional(new C) val x2 = x1.map(x => foo({ val y = x; y })) } diff --git a/tests/run/hashset.scala b/tests/run/hashset.scala index a6deb704283e..1eb5bad623a3 100644 --- a/tests/run/hashset.scala +++ b/tests/run/hashset.scala @@ -26,7 +26,7 @@ object Test extends dotty.runtime.LegacyApp { println() println("*** " + creator.hashSetType + " Strings with null") - val h2 = creator.create[String] + val h2 = creator.create[String|Null] h2 += null for (i <- 0 until 20) h2 += "" + i println("null " + (h2 contains null)) diff --git a/tests/run/hmap-covariant.scala b/tests/run/hmap-covariant.scala index 475cc6ee66fe..8bf4a6de7256 100644 --- a/tests/run/hmap-covariant.scala +++ b/tests/run/hmap-covariant.scala @@ -43,11 +43,11 @@ trait PhantomGet[K, M <: Tuple, I <: Nat] // extends PhantomAny object PhantomGet { implicit def getHead[K, V, T <: Tuple] - : PhantomGet[K, HEntry[K, V] TCons T, Zero] = null + : PhantomGet[K, HEntry[K, V] TCons T, Zero] = new PhantomGet[K, HEntry[K, V] TCons T, Zero] {} implicit def getTail[K, H, T <: Tuple, I <: Nat] (implicit t: PhantomGet[K, T, I]) - : PhantomGet[K, H TCons T, Succ[I]] = null + : PhantomGet[K, H TCons T, Succ[I]] = new PhantomGet[K, H TCons T, Succ[I]] {} } // Syntax --------------------------------------------------------------------- diff --git a/tests/run/hmap.scala b/tests/run/hmap.scala index d84419ce1e8d..ddd48ae9789b 100644 --- a/tests/run/hmap.scala +++ b/tests/run/hmap.scala @@ -43,11 +43,11 @@ trait PhantomGet[K, M <: Tuple, I <: Nat] // extends PhantomAny object PhantomGet { implicit def getHead[K, V, T <: Tuple] - : PhantomGet[K, HEntry[K, V] TCons T, Zero] = null + : PhantomGet[K, HEntry[K, V] TCons T, Zero] = new PhantomGet[K, HEntry[K, V] TCons T, Zero]{} implicit def getTail[K, H, T <: Tuple, I <: Nat] (implicit t: PhantomGet[K, T, I]) - : PhantomGet[K, H TCons T, Succ[I]] = null + : PhantomGet[K, H TCons T, Succ[I]] = new PhantomGet[K, H TCons T, Succ[I]]{} } // Syntax --------------------------------------------------------------------- diff --git a/tests/run/i2738.scala b/tests/run/i2738.scala index 5439e9e53f37..51a32939abe8 100644 --- a/tests/run/i2738.scala +++ b/tests/run/i2738.scala @@ -28,7 +28,7 @@ object Test { qux.toString() } - def thisMethodName() = Thread.currentThread().getStackTrace()(2).getMethodName + def thisMethodName() = Thread.currentThread().getStackTrace()(2).getMethodName.nn - def thisMethodsClassName() = Thread.currentThread().getStackTrace()(2).getClassName + def thisMethodsClassName() = Thread.currentThread().getStackTrace()(2).getClassName.nn } diff --git a/tests/run/i3006.scala b/tests/run/i3006.scala index 7347bc800356..79b4421f0df1 100644 --- a/tests/run/i3006.scala +++ b/tests/run/i3006.scala @@ -1,25 +1,25 @@ class Foo { def foo() = { - def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn f() } def bar() = { - def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn f() } def baz() = { - def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn f() } } class Bar { def foo() = { - def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn f() } def bar() = { - def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def f() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn f() } } diff --git a/tests/run/i3006b.scala b/tests/run/i3006b.scala index 29aab964268b..503a5da58d04 100644 --- a/tests/run/i3006b.scala +++ b/tests/run/i3006b.scala @@ -1,19 +1,19 @@ class Foo(val x: String) { def this() = this({ - def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn bar() }) def this(i: Int) = this({ - def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn bar() }) } class Bar(val x: String) { def this() = this({ - def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName + def bar() = Thread.currentThread.getStackTrace.apply(1).getMethodName.nn bar() }) } diff --git a/tests/run/i3396.scala b/tests/run/i3396.scala index fec007591d7a..4ccdbd865824 100644 --- a/tests/run/i3396.scala +++ b/tests/run/i3396.scala @@ -14,7 +14,7 @@ object Test { def main(args: Array[String]): Unit = { - implicit val taggedInt: Tagged[Int] = null + implicit val taggedInt: Tagged[Int] = new Tagged[Int] {} assert(implicitly[Foo[Int]].value) // fooDefault diff --git a/tests/run/i3539.scala b/tests/run/i3539.scala index ddef1650ceea..80798a78b6b3 100644 --- a/tests/run/i3539.scala +++ b/tests/run/i3539.scala @@ -1,16 +1,16 @@ object Test { def main(args: Array[String]): Unit = { val i2s = (x: Int) => "" - assert(i2s.asInstanceOf.asInstanceOf[AnyRef => String].apply(null) == "") + assert(i2s.asInstanceOf.asInstanceOf[AnyRef|Null => String].apply(null) == "") val i2i = (x: Int) => x + 1 - assert(i2i.asInstanceOf[AnyRef => Int].apply(null) == 1) + assert(i2i.asInstanceOf[AnyRef|Null => Int].apply(null) == 1) } } class Test { - asInstanceOf[Nothing].asInstanceOf[AnyRef => String] + asInstanceOf[Nothing].asInstanceOf[AnyRef|Null => String] - asInstanceOf[Nothing].asInstanceOf[AnyRef => String].apply(null) + asInstanceOf[Nothing].asInstanceOf[AnyRef|Null => String].apply(null) } diff --git a/tests/run/i4404c.scala b/tests/run/i4404c.scala index 29d504d57bce..4ffc4ce8128b 100644 --- a/tests/run/i4404c.scala +++ b/tests/run/i4404c.scala @@ -15,9 +15,9 @@ class C { object Test { def main(args: Array[String]) = { List((new T {}).getClass.getInterfaces.head, (new C).getClass) - .map(_.getDeclaredMethods) + .map(_.nn.getDeclaredMethods) .foreach { ms => - println(ms.exists(m => Modifier.isFinal(m.getModifiers))) + println(ms.nn.exists(m => Modifier.isFinal(m.nn.getModifiers))) } } } diff --git a/tests/run/i5067.check b/tests/run/i5067.check new file mode 100644 index 000000000000..ee69e2a9b9f7 --- /dev/null +++ b/tests/run/i5067.check @@ -0,0 +1 @@ +matches null literal diff --git a/tests/run/i5067b.check b/tests/run/i5067b.check new file mode 100644 index 000000000000..be60186f7c25 --- /dev/null +++ b/tests/run/i5067b.check @@ -0,0 +1,3 @@ +match error +match error nested +not implemented error diff --git a/tests/run/i5067b.scala b/tests/run/i5067b.scala new file mode 100644 index 000000000000..23c67db8a131 --- /dev/null +++ b/tests/run/i5067b.scala @@ -0,0 +1,33 @@ +object Test { + def main(args: Array[String]): Unit = { + class B[T] {} + object B { + def unapply[T](x: Any): Option[B[T]] = None + } + try { + val B(_) = null + } catch { + case e: MatchError => println("match error") + } + + null match { + case null => + try { + null match { + case _ if false => () + } + } catch { + case e: MatchError => println("match error nested") + } + } + + try { + ??? match { + case (_, _) => () + case _ => () + } + } catch { + case e: NotImplementedError => println("not implemented error") + } + } +} diff --git a/tests/run/imports.scala b/tests/run/imports.scala index adc173c6af62..6ecc97d84ffb 100644 --- a/tests/run/imports.scala +++ b/tests/run/imports.scala @@ -25,8 +25,8 @@ class C_ico() { o_ico.v_ico = this; import o_ico.v_ico; override def toString(): String = "C_ico"; - def method: C_ico = v_ico; - val field: C_ico = v_ico; + def method: C_ico|Null = v_ico; + val field: C_ico|Null = v_ico; check("C_ico", "v_ico ", v_ico); check("C_ico", "field ", field); @@ -35,14 +35,14 @@ class C_ico() { } object o_ico { - var v_ico: C_ico = null; + var v_ico: C_ico|Null = null; new C_ico(); } //############################################################################ object o_ioc { - var v_ioc: C_ioc = null; + var v_ioc: C_ioc|Null = null; new C_ioc(); } @@ -52,8 +52,8 @@ import o_ioc.v_ioc; class C_ioc() { o_ioc.v_ioc = this; override def toString(): String = "C_ioc"; - def method: C_ioc = v_ioc; - val field: C_ioc = v_ioc; + def method: C_ioc|Null = v_ioc; + val field: C_ioc|Null = v_ioc; check("C_ioc", "v_ioc ", v_ioc); check("C_ioc", "field ", field); @@ -64,7 +64,7 @@ class C_ioc() { //############################################################################ object o_oic { - var v_oic: C_oic = null; + var v_oic: C_oic|Null = null; new C_oic(); } @@ -73,8 +73,8 @@ import o_oic.v_oic; class C_oic() { o_oic.v_oic = this; override def toString(): String = "C_oic"; - def method: C_oic = v_oic; - val field: C_oic = v_oic; + def method: C_oic|Null = v_oic; + val field: C_oic|Null = v_oic; check("C_oic", "v_oic ", v_oic); check("C_oic", "field ", field); diff --git a/tests/run/is-valid-num.scala b/tests/run/is-valid-num.scala index 5a4a0503da59..8d16fd692832 100644 --- a/tests/run/is-valid-num.scala +++ b/tests/run/is-valid-num.scala @@ -203,7 +203,7 @@ object Test { } def checkBigInt(bi: BigInt): Unit = { - val bd = BigDecimal(bi, java.math.MathContext.UNLIMITED) + val bd = BigDecimal(bi, java.math.MathContext.UNLIMITED.nn) val isByte = bi >= Byte.MinValue && bi <= Byte.MaxValue val isShort = bi >= Short.MinValue && bi <= Short.MaxValue val isChar = bi >= Char.MinValue && bi <= Char.MaxValue diff --git a/tests/run/matchnull.scala b/tests/run/matchnull.scala index 2cc8550d4743..063e4e0b6e68 100644 --- a/tests/run/matchnull.scala +++ b/tests/run/matchnull.scala @@ -1,8 +1,8 @@ object Test { - def f1 = null match { case x: AnyRef => 1 case _ => -1 } + def f1 = (null: AnyRef|Null) match { case x: AnyRef => 1 case _ => -1 } def f2(x: Any) = x match { case 52 => 1 ; case null => -1 ; case _ => 0 } - def f3(x: AnyRef) = x match { case x: String => 1 ; case List(_) => 0 ; case null => -1 ; case _ => -2 } + def f3(x: AnyRef|Null) = x match { case x: String => 1 ; case List(_) => 0 ; case null => -1 ; case _ => -2 } def main(args: Array[String]): Unit = { println(f1) diff --git a/tests/run/mixin-bridge-methods.scala b/tests/run/mixin-bridge-methods.scala index e0340ebb125a..bc6df0f2b137 100644 --- a/tests/run/mixin-bridge-methods.scala +++ b/tests/run/mixin-bridge-methods.scala @@ -9,6 +9,6 @@ class Sub extends Foo { object Test { def main(args: Array[String]): Unit = { val ms = classOf[Sub].getDeclaredMethods - assert(ms forall (x => !x.isBridge), ms mkString " ") + assert(ms forall (x => !x.nn.isBridge), ms mkString " ") } } diff --git a/tests/run/no-useless-forwarders.scala b/tests/run/no-useless-forwarders.scala index 699295027300..c65cd82c6ca9 100644 --- a/tests/run/no-useless-forwarders.scala +++ b/tests/run/no-useless-forwarders.scala @@ -9,9 +9,9 @@ trait B { object Test extends A with B{ def main(args: Array[String]) = { - assert(!this.getClass.getDeclaredMethods.exists{x: java.lang.reflect.Method => x.getName == "foo"}, + assert(!this.getClass.getDeclaredMethods.exists{x: java.lang.reflect.Method|Null => x.nn.getName == "foo"}, "no forwarder is needed here") - assert(!this.getClass.getDeclaredMethods.exists{x: java.lang.reflect.Method => x.getName == "bar"}, + assert(!this.getClass.getDeclaredMethods.exists{x: java.lang.reflect.Method|Null => x.nn.getName == "bar"}, "no forwarder is needed here") } } diff --git a/tests/run/nothing-lazy-val.scala b/tests/run/nothing-lazy-val.scala index 3e7aada0da2e..4efb4e3765b9 100644 --- a/tests/run/nothing-lazy-val.scala +++ b/tests/run/nothing-lazy-val.scala @@ -14,7 +14,7 @@ object Test { // TODO: Erase // Currently not erasing fields for lazy vals - assert(classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") + assert(classOf[Foo].getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") } diff --git a/tests/run/nothing-val.scala b/tests/run/nothing-val.scala index 104be9e435b6..03fe75e6e87f 100644 --- a/tests/run/nothing-val.scala +++ b/tests/run/nothing-val.scala @@ -10,7 +10,7 @@ object Test { case e: NotImplementedError => println("???") } - assert(!classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!classOf[Foo].getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } diff --git a/tests/run/nothing-var.scala b/tests/run/nothing-var.scala index bb31253439a1..6453c16c7f32 100644 --- a/tests/run/nothing-var.scala +++ b/tests/run/nothing-var.scala @@ -16,7 +16,7 @@ object Test { case e: NotImplementedError => println("???") } - assert(!classOf[Foo].getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!classOf[Foo].getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } diff --git a/tests/run/null-and-intersect.scala b/tests/run/null-and-intersect.scala index 7266dabe6df4..8c9afeb7aa56 100644 --- a/tests/run/null-and-intersect.scala +++ b/tests/run/null-and-intersect.scala @@ -3,17 +3,17 @@ object Test { class Bippy extends Immutable with Immortal class Boppy extends Immutable - def f[T](x: Traversable[T]) = x match { + def f[T](x: Traversable[T]|Null) = x match { case _: Map[_, _] => 3 case _: Seq[_] => 2 case _: Iterable[_] => 1 case _ => 4 } - def g(x: Bippy) = x match { + def g(x: Bippy|Null) = x match { case _: Immutable with Immortal => 1 case _ => 2 } - def h(x: Immutable) = x match { + def h(x: Immutable|Null) = x match { case _: Immortal => 1 case _ => 2 } diff --git a/tests/run/null-hash.scala b/tests/run/null-hash.scala index 9b1f28b083c1..961ee64434dd 100644 --- a/tests/run/null-hash.scala +++ b/tests/run/null-hash.scala @@ -1,15 +1,15 @@ object Test { - def f1 = List(5, 10, null: String).## + def f1 = List(5, 10, null: String|Null).## def f2(x: Any) = x.## - def f3 = ((55, "abc", null: List[Int])).## + def f3 = ((55, "abc", null: List[Int]|Null)).## def main(args: Array[String]): Unit = { f1 f2(null) - f2(null: String) + f2(null: String|Null) f3 null.## (null: Any).## - (null: String).## + (null: String|Null).## } } diff --git a/tests/run/null-lazy-val.scala b/tests/run/null-lazy-val.scala index fb7e78f730f0..c61329ee3470 100644 --- a/tests/run/null-lazy-val.scala +++ b/tests/run/null-lazy-val.scala @@ -10,7 +10,7 @@ object Test { // TODO: Erase // Currently not erasing fields for lazy vals - assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") + assert(f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") } diff --git a/tests/run/null-val.scala b/tests/run/null-val.scala index aa2637ee1a42..79fb259278d4 100644 --- a/tests/run/null-val.scala +++ b/tests/run/null-val.scala @@ -4,7 +4,7 @@ object Test { val f = new Foo println(f.foo) println(f.foo) - assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } } diff --git a/tests/run/null-var.scala b/tests/run/null-var.scala index 56bca4b9bcde..58c23980d5d8 100644 --- a/tests/run/null-var.scala +++ b/tests/run/null-var.scala @@ -9,7 +9,7 @@ object Test { null } println(f.foo) - assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } } diff --git a/tests/run/nullInstanceEval.scala b/tests/run/nullInstanceEval.scala index 47b7153eccf4..2c4ffbf0521e 100644 --- a/tests/run/nullInstanceEval.scala +++ b/tests/run/nullInstanceEval.scala @@ -1,5 +1,5 @@ object Test extends App { val x = null assert(!x.isInstanceOf[String]) - assert(!(x: AnyRef).isInstanceOf[String]) + assert(!(x: Any).isInstanceOf[String]) } diff --git a/tests/run/numbereq.scala b/tests/run/numbereq.scala index 770bf50fee15..c7db6ccfaf7b 100644 --- a/tests/run/numbereq.scala +++ b/tests/run/numbereq.scala @@ -23,7 +23,7 @@ object Test { List( // FIXME: breaks ycheck, broken type inferencing. Gets a large union type // instead of list of Number - List(BigDecimal(x, java.math.MathContext.UNLIMITED): Number), + List(BigDecimal(x, java.math.MathContext.UNLIMITED.nn): Number), List(x), if (x.isValidDouble) List(new java.lang.Double(x.toDouble)) else Nil, if (x.isValidFloat) List(new java.lang.Float(x.toFloat)) else Nil, diff --git a/tests/run/opaque-immutable-array-xm.scala b/tests/run/opaque-immutable-array-xm.scala index ffd07c59047e..eb6fd731306c 100644 --- a/tests/run/opaque-immutable-array-xm.scala +++ b/tests/run/opaque-immutable-array-xm.scala @@ -2,6 +2,7 @@ import scala.reflect.ClassTag object Test extends App { import java.util.Arrays + import scala.NonNull.ArrayConversions._ opaque type IArray[A1] = Array[A1] @@ -16,7 +17,7 @@ object Test extends App { // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { - val arr = Arrays.copyOf(ia, (ia: Array[A]).length) + val arr: Array[A] = Arrays.copyOf(ia, ia.length).nn scala.util.Sorting.quickSort(arr) arr } @@ -48,4 +49,4 @@ object Test extends App { val xs: IArray[Long] = IArray(1L, 2L, 3L) assert(binaryIndexOf(xs, 2L) == 1) assert(binaryIndexOf(xs, 4L) < 0) -} \ No newline at end of file +} diff --git a/tests/run/patmat-finally.scala b/tests/run/patmat-finally.scala index 895fb79f79de..5e66cf0077ff 100644 --- a/tests/run/patmat-finally.scala +++ b/tests/run/patmat-finally.scala @@ -1,6 +1,6 @@ /** Test pattern matching and finally, see SI-5929. */ object Test extends dotty.runtime.LegacyApp { - def bar(s1: Object, s2: Object): Unit = { + def bar(s1: Object|Null, s2: Object|Null): Unit = { s1 match { case _ => } diff --git a/tests/run/patmat-option-named.scala b/tests/run/patmat-option-named.scala index b27d07107709..0178a9a2da08 100644 --- a/tests/run/patmat-option-named.scala +++ b/tests/run/patmat-option-named.scala @@ -1,4 +1,4 @@ -case class HasSingleField(f: HasSingleField) +case class HasSingleField(f: HasSingleField|Null) object Test { diff --git a/tests/run/runtime.scala b/tests/run/runtime.scala index 89348b294db8..e81498917bd3 100644 --- a/tests/run/runtime.scala +++ b/tests/run/runtime.scala @@ -134,7 +134,7 @@ object Test3Test { + "found: " + actual1 + " - " + actual1); def test(args: Array[String]): Unit = { - val foo1: AnyRef = null; + val foo1: AnyRef|Null = null; val foo2: AnyRef = new Foo(); val foo3: AnyRef = new Foo(); diff --git a/tests/run/sams.scala b/tests/run/sams.scala new file mode 100644 index 000000000000..d691ff4e5494 --- /dev/null +++ b/tests/run/sams.scala @@ -0,0 +1,154 @@ +trait X { def foo(x: Int): Int; def bar = foo(2) } + +trait T { + var f = 2 + def foo(x: Int): Int +} + +trait U extends T + +trait V extends Exception { def foo(x: Int): Int } + +trait Y extends X { + def baz = super.bar +} + +trait Z { + def foo(x: Int): Int; 42 +} +trait ZZ extends Z + +abstract class C { + def foo(x: Int): Int + trait I { def foo(x: Int): Int } +} + +object Test { + + + def main(args: Array[String]): Unit = { + { + val x: X = (x: Int) => 2 // should be a closure + assert(x.foo(100) == 2) + } + + { + val t: T = (x: Int) => 2 // needs to be an anonymous class because of defined field + assert(t.foo(200) == 2) + } + + { + val u: U = (x: Int) => 2 // needs to be an anonymous class because of inherited field + assert(u.foo(100) == 2) + } + + { + val v: V = (x: Int) => 2 // needs to be an anonymous class because the trait extends a non-object class + assert(v.foo(100) == 2) + } + + { + val y: Y = (x: Int) => 2 // needs to be an anonymous class because of super accessor + assert(y.foo(100) == 2) + } + + { + val z: Z = (x: Int) => 2 // needs to be an anonymous class because trait has initialization code + val zz: ZZ = (x: Int) => 3 // needs to be an anonymous class becaiuse trait has initialization code + + assert(z.foo(100) == 2) + assert(zz.foo(100) == 3) + } + + { + val c: C = (x: Int) => 2 // needs to be an anonymous class because C is not a trait + val ci: c.I = (x: Int) => 2 // needs to be an anonymous class because it needs an outer pointer + + assert(c.foo(100) == 2) + assert(ci.foo(100) == 2) + } + + // Partial functions + val pf: PartialFunction[Int, Int] = { + case 1 => 1 + case 2 => 2 + } + + val qf: PartialFunction[(Int, String), Int] = { + case (1, "abc") => 1 + case _ => 2 + } + + val rf: PartialFunction[(Int, AnyRef), Int] = { + case (_: Int, _: String) => 1 + case _ => 2 + } + + val sf: PartialFunction[Any, Int] = { + case x: String if x == "abc" => 1 + } + + assert(pf(1) == 1) + assert(qf((1, "abc")) == 1) + assert(rf(1, "hello") == 1) + assert(sf("abc") == 1) + + // Implicit nullary function + { + val f: implicit () => Int = 42 + assert (f == 42) + } + + // With null + { + val f: (Int => Int)|Null = (x: Int) => 2 + assert(f.nn(100) == 2) + } + + { + val pf: PartialFunction[Int, Int]|Null = { + case 1 => 1 + } + assert(pf.nn(1) == 1) + } + + { + val x: X|Null = (x: Int) => 2 + assert(x.nn.foo(100) == 2) + } + + { + val t: T|Null = (x: Int) => 2 + assert(t.nn.foo(200) == 2) + } + + { + val u: U|Null = (x: Int) => 2 + assert(u.nn.foo(100) == 2) + } + + { + val v: V|Null = (x: Int) => 2 + assert(v.nn.foo(100) == 2) + } + + { + val y: Y|Null = (x: Int) => 2 + assert(y.nn.foo(100) == 2) + } + + { + val z: Z|Null = (x: Int) => 2 + val zz: ZZ|Null = (x: Int) => 3 + + assert(z.nn.foo(100) == 2) + assert(zz.nn.foo(100) == 3) + } + + { + val c: C|Null = (x: Int) => 2 + assert(c.nn.foo(100) == 2) + } + } +} + diff --git a/tests/run/t1005.scala b/tests/run/t1005.scala index 562e2e4c6d50..317c02cc4c37 100644 --- a/tests/run/t1005.scala +++ b/tests/run/t1005.scala @@ -5,11 +5,11 @@ object Test class Bar[T](x : Array[T]) { def bar = x.asInstanceOf[Array[AnyRef]] } object FromMono{ - def mainer(args : Array[String]) = (new Foo[AnyRef](Array[AnyRef]("Halp!"))).bar + def mainer(args : Array[String]|Null) = (new Foo[AnyRef](Array[AnyRef]("Halp!"))).bar } object FromPoly{ - def mainer(args : Array[String]) = (new Bar[AnyRef](Array[AnyRef]("Halp!"))).bar + def mainer(args : Array[String]|Null) = (new Bar[AnyRef](Array[AnyRef]("Halp!"))).bar } def main(args: Array[String]): Unit = { diff --git a/tests/run/t1335.scala b/tests/run/t1335.scala index 047f7b566a70..89f72270dc5d 100644 --- a/tests/run/t1335.scala +++ b/tests/run/t1335.scala @@ -3,7 +3,7 @@ case class MyTuple(a: Int, b: Int) object Test { def main(args: Array[String]): Unit = try { - val mt: MyTuple = null + val mt: MyTuple|Null = null val MyTuple(a, b) = mt } catch { case e: MatchError => () diff --git a/tests/run/t1718.scala b/tests/run/t1718.scala index e6f52025448a..ee86122216e9 100644 --- a/tests/run/t1718.scala +++ b/tests/run/t1718.scala @@ -1,10 +1,10 @@ object Test extends dotty.runtime.LegacyApp{ - def matchesNull[T](mightBeNull: Array[T]): Boolean = mightBeNull match { + def matchesNull[T](mightBeNull: Array[T]|Null): Boolean = mightBeNull match { case null => true case x => false } - val nullArray: Array[String] = null + val nullArray: Array[String]|Null = null println(matchesNull(nullArray)) } diff --git a/tests/run/t1987.scala b/tests/run/t1987.scala index f5abc262cc9f..592d2afd4bcc 100644 --- a/tests/run/t1987.scala +++ b/tests/run/t1987.scala @@ -56,7 +56,7 @@ package bip { object Test { def main(args: Array[String]): Unit = { - foo.bar.Main.main(null) - bip.bar.Main.main(null) + foo.bar.Main.main(Array.empty[String]) + bip.bar.Main.main(Array.empty[String]) } } diff --git a/tests/run/t2755.scala b/tests/run/t2755.scala index 8d10b567346b..fb07d3425d48 100644 --- a/tests/run/t2755.scala +++ b/tests/run/t2755.scala @@ -10,7 +10,7 @@ object Test { case x: Array[_] => 6 case _ => 7 } - def f2(a: Array[_]) = a match { + def f2(a: Array[_]|Null) = a match { case x: Array[Int] => x(0) case x: Array[Double] => 2 case x: Array[Float] => x.sum.toInt @@ -19,7 +19,7 @@ object Test { case x: Array[_] => 6 case _ => 7 } - def f3[T](a: Array[T]) = a match { + def f3[T](a: Array[T]|Null) = a match { case x: Array[Int] => x(0) case x: Array[Double] => 2 case x: Array[Float] => x.sum.toInt diff --git a/tests/run/t3126.scala b/tests/run/t3126.scala index 36322bf89696..f91ba48c19b7 100644 --- a/tests/run/t3126.scala +++ b/tests/run/t3126.scala @@ -1,9 +1,12 @@ object Test { case class C(x: Int) - val v: Some[Int] = null + val v1: Some[Int] | Null = null + val v2: Some[Int] = null.asInstanceOf[Some[Int]] def main(args: Array[String]): Unit = { - try C.unapply(null) catch { case _: MatchError => } - try v match { case Some(1) => } catch { case _: MatchError => } + try C.unapply(null.asInstanceOf[C]) catch { case _: MatchError => } + try (v1: @unchecked) match { case Some(1) => } catch { case _: MatchError => } + try (v2: @unchecked) match { case Some(1) => } catch { case _: NullPointerException => } } } + diff --git a/tests/run/t3452a/S_1.scala b/tests/run/t3452a/S_1.scala index 791faf42fa40..38e90de9048b 100644 --- a/tests/run/t3452a/S_1.scala +++ b/tests/run/t3452a/S_1.scala @@ -3,7 +3,7 @@ abstract class BulkSearch { type Rel <: Relation [R] type Corr <: Correspondence[R] - def searchFor(input: Rel): Mapping[Corr] = { println("BulkSearch.searchFor called.") ; null } + def searchFor(input: Rel): Mapping[Corr] = { println("BulkSearch.searchFor called.") ; new Mapping[Corr] } } object BulkSearchInstance extends BulkSearch { diff --git a/tests/run/t3452a/S_3.scala b/tests/run/t3452a/S_3.scala index aaa898dcde94..73adb7976f75 100644 --- a/tests/run/t3452a/S_3.scala +++ b/tests/run/t3452a/S_3.scala @@ -1,5 +1,6 @@ object Test { def main(args: Array[String]): Unit = { + import scala.NonNull.ArrayConversions._ J_2.main(args) } } diff --git a/tests/run/t3452b-bcode/S_1.scala b/tests/run/t3452b-bcode/S_1.scala index a209f1203539..0e8036ada023 100644 --- a/tests/run/t3452b-bcode/S_1.scala +++ b/tests/run/t3452b-bcode/S_1.scala @@ -1,14 +1,14 @@ trait Search[M] { def search(input: M): C[Int] = { println("Search received: " + input) - null + new C[Int] {} } } class SearchC[M] { def searchC(input: M): C[Int] = { println("SearchC received: " + input) - null + new C[Int] {} } } diff --git a/tests/run/t3452h.scala b/tests/run/t3452h.scala index d61fb065ba99..2f970d58a9b5 100644 --- a/tests/run/t3452h.scala +++ b/tests/run/t3452h.scala @@ -1,12 +1,13 @@ class Mix extends Foo with Bar { f; } trait T abstract class Foo { - class I extends T + type I = I1|Null + class I1 extends T def f: I f } trait Bar { - type I >: Null <: T + type I >: Null <: T|Null def f: I = null f def gobble: I = null diff --git a/tests/run/t4023.scala b/tests/run/t4023.scala index 68f5ec815995..83b548ac1b85 100644 --- a/tests/run/t4023.scala +++ b/tests/run/t4023.scala @@ -15,9 +15,9 @@ object Test { // sortBy(_.getName) introduces additional classes which we don't want to see in C, // so we call sortBy outside of C. object TestHelper { - val valuesTry1 = C.classes1.sortBy(_.getName) - val valuesTry2 = C.classes2.sortBy(_.getName) - val valuesTry3 = C.classes3.sortBy(_.getName) + val valuesTry1 = C.classes1.sortBy(_.nn.getName.nn) + val valuesTry2 = C.classes2.sortBy(_.nn.getName.nn) + val valuesTry3 = C.classes3.sortBy(_.nn.getName.nn) } def main(args: Array[String]): Unit = { diff --git a/tests/run/t4827.scala b/tests/run/t4827.scala index 7270cf169def..d4eef0507714 100644 --- a/tests/run/t4827.scala +++ b/tests/run/t4827.scala @@ -3,7 +3,7 @@ object Test { } trait CommonTrait { - def foo(): String = null + def foo(): String = "hello" } class Foo @@ -11,5 +11,5 @@ class Foo object Foo { def goo() = new Foo() with CommonTrait - def foo(): String = null + def foo(): String = "world" } diff --git a/tests/run/t5604.scala b/tests/run/t5604.scala index eccad1639bde..2b55af130c4e 100644 --- a/tests/run/t5604.scala +++ b/tests/run/t5604.scala @@ -29,7 +29,7 @@ package foo { } package bar { object Main { - def main(args:Array[String]): Unit = { + def main(args:Array[String]|Null): Unit = { duh(33L) duh(3.0d) foo.bar.duh(33L) diff --git a/tests/run/t5610a.scala b/tests/run/t5610a.scala index 3787c098455d..362e288eafe7 100644 --- a/tests/run/t5610a.scala +++ b/tests/run/t5610a.scala @@ -1,13 +1,13 @@ object Test extends dotty.runtime.LegacyApp { - class Result(_str: => String) { + class Result(_str: => String|Null) { lazy val str = _str } - def foo(str: => String)(i: Int) = new Result(str) + def foo(str: => String|Null)(i: Int) = new Result(str) def bar(f: Int => Result) = f(42) - var test: String = null + var test: String|Null = null val result = bar(foo(test)) test = "bar" diff --git a/tests/run/t603.scala b/tests/run/t603.scala index 84a224a40a91..f3e2d49ecf8a 100644 --- a/tests/run/t603.scala +++ b/tests/run/t603.scala @@ -2,12 +2,12 @@ object forceDelay { import scala.language.implicitConversions class Susp[+A](lazyValue: => A) extends Function0[A] { - private var func: () => Any = () => lazyValue + private var func: (() => Any)|Null = () => lazyValue private var value: Any = null override def apply() = { if (func != null) { - value = func().asInstanceOf[A] + value = func.nn().asInstanceOf[A] func = null } value.asInstanceOf[A] diff --git a/tests/run/t6168b/main.scala b/tests/run/t6168b/main.scala index b8871ba1a83d..e6723c13debf 100644 --- a/tests/run/t6168b/main.scala +++ b/tests/run/t6168b/main.scala @@ -4,5 +4,5 @@ object Test extends dotty.runtime.LegacyApp { JavaTest.main(null) var a1 : SomeClass = new SomeClass - var b1 : Object = a1.f.set(23) + var b1 : Object|Null = a1.f.set(23) } diff --git a/tests/run/t6260-delambdafy.scala b/tests/run/t6260-delambdafy.scala index a4a3cfc8ff02..14a47e2151a5 100644 --- a/tests/run/t6260-delambdafy.scala +++ b/tests/run/t6260-delambdafy.scala @@ -4,7 +4,7 @@ object Test { val f = (x: C[Any]) => {println(s"f($x)"); x} def main(args: Array[String]): Unit = { f(new C(".")) - val methods = f.getClass.getDeclaredMethods.map(_.getName).sorted + val methods = f.getClass.getDeclaredMethods.map(_.nn.getName.nn).sorted println("") println(methods.mkString("\n")) } diff --git a/tests/run/t6443b.scala b/tests/run/t6443b.scala index 7633bedfc3fc..bb507f26d48c 100644 --- a/tests/run/t6443b.scala +++ b/tests/run/t6443b.scala @@ -1,16 +1,17 @@ trait A { - type D >: Null <: C + type D >: Null <: C|Null def foo(d: D)(d2: d.type): Unit trait C { def bar: Unit = foo(null)(null) } } object B extends A { - class D extends C + class D1 extends C + type D = D1|Null def foo(d: D)(d2: d.type): Unit = () // Bridge method required here! } object Test extends dotty.runtime.LegacyApp { - new B.D().bar + new B.D1().bar } diff --git a/tests/run/t6488.scala b/tests/run/t6488.scala index 559164044242..ec01a94c5cb8 100644 --- a/tests/run/t6488.scala +++ b/tests/run/t6488.scala @@ -31,7 +31,7 @@ object Test { // fork the data spewer, wait for input, then destroy the process def test(): Unit = { - val f = new File(javaHome, "bin").listFiles.sorted filter (_.getName startsWith "java") find (_.canExecute) getOrElse { + val f = new File(javaHome, "bin").listFiles.map(_.nn).sorted filter (_.getName startsWith "java") find (_.canExecute) getOrElse { // todo signal test runner that test is skipped new File("/bin/ls") // innocuous } diff --git a/tests/run/t6706.scala b/tests/run/t6706.scala index 905494ca8dc1..0c3a0f005e23 100644 --- a/tests/run/t6706.scala +++ b/tests/run/t6706.scala @@ -1,6 +1,6 @@ object Test { - var name = "foo" + 1 - var s1 = Symbol(name) + var name: String|Null = "foo" + 1 + var s1: Symbol|Null = Symbol(name.nn) s1 = null System.gc val s2 = Symbol("foo1") diff --git a/tests/run/t6793.scala b/tests/run/t6793.scala index 3d8492c10469..78b15140bf8c 100644 --- a/tests/run/t6793.scala +++ b/tests/run/t6793.scala @@ -5,5 +5,5 @@ object Test extends dotty.runtime.LegacyApp { new b.C2("x") val c2Fields = classOf[b.C2].getDeclaredFields - assert(c2Fields.size == 1, c2Fields.map(_.getName).toList) + assert(c2Fields.size == 1, c2Fields.map(_.nn.getName).toList) } diff --git a/tests/run/t6928-run.scala b/tests/run/t6928-run.scala index 87a8884d60ec..b6046f51046e 100644 --- a/tests/run/t6928-run.scala +++ b/tests/run/t6928-run.scala @@ -1,4 +1,4 @@ -abstract class A( val someAs: A* ) { +abstract class A( val someAs: A|Null* ) { override def toString = someAs.length + " As" } object B extends A(null, null, null) diff --git a/tests/run/t7120b.scala b/tests/run/t7120b.scala index e5b546a90e07..aab76d672f50 100644 --- a/tests/run/t7120b.scala +++ b/tests/run/t7120b.scala @@ -20,11 +20,11 @@ class CHK extends BaseHK[BaseHK.Id, String] { object Test extends dotty.runtime.LegacyApp { val c = new C val d = new c.D() - val meth = d.getClass.getMethods.find(_.getName == "foo").get + val meth = d.getClass.getMethods.find(_.nn.getName == "foo").get println(meth) val chk = new CHK val dhk = new chk.D() - val methhk = d.getClass.getMethods.find(_.getName == "foo").get + val methhk = d.getClass.getMethods.find(_.nn.getName == "foo").get println(methhk) } diff --git a/tests/run/t7214.scala b/tests/run/t7214.scala index d73fe6dd5f8f..3496812d8ab0 100644 --- a/tests/run/t7214.scala +++ b/tests/run/t7214.scala @@ -35,7 +35,7 @@ class Crash { type CdotT = c.T type C2dotT = c2.T - val outerField = t.getClass.getDeclaredFields.find(_.getName contains ("outer")).get + val outerField = t.getClass.getDeclaredFields.find(_.nn.getName contains ("outer")).get.nn outerField.setAccessible(true) (t: Any) match { diff --git a/tests/run/t7231.scala b/tests/run/t7231.scala index 63ed16097348..9ae33909defe 100644 --- a/tests/run/t7231.scala +++ b/tests/run/t7231.scala @@ -1,8 +1,8 @@ object Test extends dotty.runtime.LegacyApp { val bar: Null = null - def foo(x: Array[Int]) = x - def baz(x: String) = x + def foo(x: Array[Int]|Null) = x + def baz(x: String|Null) = x // first line was failing println(foo(bar)) diff --git a/tests/run/t8087.scala b/tests/run/t8087.scala index 7306039c6509..0532efe9566b 100644 --- a/tests/run/t8087.scala +++ b/tests/run/t8087.scala @@ -7,6 +7,6 @@ class Bar extends Foo object Test extends dotty.runtime.LegacyApp { classOf[Bar].getDeclaredFields.foreach(f => { - assert(java.lang.reflect.Modifier.isVolatile(f.getModifiers), f.getName) + assert(java.lang.reflect.Modifier.isVolatile(f.nn.getModifiers), f.nn.getName) }) } diff --git a/tests/run/t8177f.scala b/tests/run/t8177f.scala index 6f9a68c11b75..42153e0cede3 100644 --- a/tests/run/t8177f.scala +++ b/tests/run/t8177f.scala @@ -7,7 +7,7 @@ class A[T](final val x: Thing { type A = T }) { def x3: x.A = x.p } // all result types should be inferred as Int -class B extends A[Int](null) { +class B extends A[Int](new Thing { type A = Int }) { def y1 = x1 def y2 = x2 val y3 = x3 // before SI-8177, this lead to a signature that erased to java.lang.Object @@ -15,6 +15,14 @@ class B extends A[Int](null) { object Test extends dotty.runtime.LegacyApp { - val methods = classOf[B].getDeclaredMethods.sortBy(_.getName) - assert(methods.forall(_.toGenericString.startsWith("public int"))) + val methods = classOf[B].getDeclaredMethods.sortBy(_.nn.getName.nn) + // B's methods are + // private static Thing B.B$superArg$1() + // public int B.y1() + // public int B.y2() + // public int B.y3() + assert(methods.forall { m => + val name = m.nn.toGenericString + name.startsWith("private") || name.startsWith("public int") + }) } diff --git a/tests/run/t8188.scala b/tests/run/t8188.scala index 9ee542f2ae1a..20d12f0bc5c3 100644 --- a/tests/run/t8188.scala +++ b/tests/run/t8188.scala @@ -9,7 +9,7 @@ object Test { def ser[T](o: T): Array[Byte] = { val baos = new ByteArrayOutputStream() new ObjectOutputStream(baos).writeObject(o) - baos.toByteArray() + baos.toByteArray().nn } def deser[T](bs: Array[Byte]): T = diff --git a/tests/run/t8197.scala b/tests/run/t8197.scala index b510f96f1b38..ebcea88b0fa6 100644 --- a/tests/run/t8197.scala +++ b/tests/run/t8197.scala @@ -1,10 +1,10 @@ // SI-8197, see also SI-4592 and SI-4728 -class A +class A(val tag: String) class B -class Foo(val x: A = null) { +class Foo(val x: A = new A("default")) { def this(bla: B*) = { - this(new A) + this(new A("varargs")) } } @@ -12,5 +12,5 @@ object Test extends dotty.runtime.LegacyApp { // both constructors of `Foo` are applicable. Overloading resolution // will eliminate the alternative that uses a default argument, therefore // the vararg constructor is chosen. - assert((new Foo).x != null) + assert((new Foo).x.tag == "varargs") } diff --git a/tests/run/t8197b.scala b/tests/run/t8197b.scala index a9012ddf9de4..61d588a2f2ee 100644 --- a/tests/run/t8197b.scala +++ b/tests/run/t8197b.scala @@ -1,6 +1,6 @@ object O { def foo[T](t: T) = 0 - def foo(s: String)(implicit i: DummyImplicit = null) = 1 + def foo(s: String)(implicit i: DummyImplicit|Null = null) = 1 } object Test extends dotty.runtime.LegacyApp { diff --git a/tests/run/t8233-bcode.scala b/tests/run/t8233-bcode.scala index 771e4cf0c1b0..e4543a7604bb 100644 --- a/tests/run/t8233-bcode.scala +++ b/tests/run/t8233-bcode.scala @@ -1,5 +1,5 @@ object Test { - def bar(s: String) = s; + def bar(s: String|Null) = s; val o: Option[Null] = None def nullReference: Unit = { val a: Null = o.get diff --git a/tests/run/t8233.scala b/tests/run/t8233.scala index 3896a7cc6b12..c9455f7795b8 100644 --- a/tests/run/t8233.scala +++ b/tests/run/t8233.scala @@ -1,5 +1,5 @@ object Test { - def bar(s: String) = s; + def bar(s: String|Null) = s; val o: Option[Null] = None def nullReference: Unit = { val a: Null = o.get diff --git a/tests/run/t8346.scala b/tests/run/t8346.scala index 1c34ab35f749..9089cef9f86d 100644 --- a/tests/run/t8346.scala +++ b/tests/run/t8346.scala @@ -7,7 +7,7 @@ object Test extends dotty.runtime.LegacyApp { def sctor[A <: Set[Int]](f: Int => A)(implicit A: ClassTag[A]) : (String, Int => Set[Int]) = - (A.runtimeClass.getSimpleName, f) + (A.runtimeClass.getSimpleName.nn, f) val inits: Seq[(String, Int => Set[Int])] = { import collection.immutable.{Seq => _, _} diff --git a/tests/run/t8601b.scala b/tests/run/t8601b.scala index 3816e0b83f31..f8659adc3eaf 100644 --- a/tests/run/t8601b.scala +++ b/tests/run/t8601b.scala @@ -1,6 +1,6 @@ object Test { - def len(x: Array[String]): Unit = x.length - def load(x: Array[String]): Unit = x(0) + def len(x: Array[String]|Null): Unit = x.nn.length + def load(x: Array[String]|Null): Unit = x.nn(0) def newarray(i: Int): Unit = new Array[Int](i) def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } diff --git a/tests/run/t8601c.scala b/tests/run/t8601c.scala index 946a4d4b69b1..6120262fc3ec 100644 --- a/tests/run/t8601c.scala +++ b/tests/run/t8601c.scala @@ -1,6 +1,6 @@ object Test { - def loadField(x: scala.runtime.IntRef): Unit = x.elem - def storeField(x: scala.runtime.IntRef): Unit = x.elem = 42 + def loadField(x: scala.runtime.IntRef|Null): Unit = x.nn.elem + def storeField(x: scala.runtime.IntRef|Null): Unit = x.nn.elem = 42 def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } diff --git a/tests/run/t8601d.scala b/tests/run/t8601d.scala index a6962847cb83..7db0e90bd2b4 100644 --- a/tests/run/t8601d.scala +++ b/tests/run/t8601d.scala @@ -3,6 +3,6 @@ object Test { def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => } def main(args: Array[String]): Unit = { - check(monitor(null)) + check(monitor(null.asInstanceOf[AnyRef])) } } diff --git a/tests/run/try.scala b/tests/run/try.scala index 785f0153a7a5..bd3f81eb7ac8 100644 --- a/tests/run/try.scala +++ b/tests/run/try.scala @@ -38,7 +38,7 @@ object Test extends AnyRef with App { Console.println(x + n); } - var instance: AnyRef = null; + var instance: AnyRef|Null = null; def try4 = { if (instance == null) { @@ -91,7 +91,7 @@ object Test extends AnyRef with App { def get = null } - var sekw : SekwencjaArray = + var sekw : SekwencjaArray|Null = try { null } catch { @@ -99,7 +99,7 @@ object Test extends AnyRef with App { } new AnyRef { - def getValueAt(row:Int, col:Int) = sekw.get + def getValueAt(row:Int, col:Int) = sekw.nn.get } } diff --git a/tests/run/tryPatternMatch.scala b/tests/run/tryPatternMatch.scala index db01ec172288..e1f287a3b910 100644 --- a/tests/run/tryPatternMatch.scala +++ b/tests/run/tryPatternMatch.scala @@ -3,7 +3,7 @@ import java.util.concurrent.TimeoutException object IAE { def unapply(e: Exception): Option[String] = - if (e.isInstanceOf[IllegalArgumentException] && e.getMessage != null) Some(e.getMessage) + if (e.isInstanceOf[IllegalArgumentException] && e.getMessage != null) Some(e.getMessage.nn) else None } diff --git a/tests/run/unit-lazy-val.scala b/tests/run/unit-lazy-val.scala index a93d28c81ade..c5538538b7e6 100644 --- a/tests/run/unit-lazy-val.scala +++ b/tests/run/unit-lazy-val.scala @@ -10,7 +10,7 @@ object Test { // TODO: Erase // Currently not erasing fields for lazy vals - assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") + assert(f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?") } diff --git a/tests/run/unit-val.scala b/tests/run/unit-val.scala index 58175d659ff8..d3b1bb2d5dab 100644 --- a/tests/run/unit-val.scala +++ b/tests/run/unit-val.scala @@ -5,7 +5,7 @@ object Test { println(1) f.foo f.foo - assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } } diff --git a/tests/run/unit-var.scala b/tests/run/unit-var.scala index c024813a887f..703a5d0b4f00 100644 --- a/tests/run/unit-var.scala +++ b/tests/run/unit-var.scala @@ -8,7 +8,7 @@ object Test { println("foo3") } f.foo - assert(!f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "field foo not erased") + assert(!f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "field foo not erased") } } diff --git a/tests/run/unit-volatile-var.scala b/tests/run/unit-volatile-var.scala index 1c7a77fd6d40..7e875fe2d1ed 100644 --- a/tests/run/unit-volatile-var.scala +++ b/tests/run/unit-volatile-var.scala @@ -8,7 +8,7 @@ object Test { println("foo3") } f.foo - assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "@volatile field foo erased") + assert(f.getClass.getDeclaredFields.exists(_.nn.getName.startsWith("foo")), "@volatile field foo erased") } } diff --git a/tests/run/unit_erasure.scala b/tests/run/unit_erasure.scala index 51ccf0a16668..0a75772c8f4d 100644 --- a/tests/run/unit_erasure.scala +++ b/tests/run/unit_erasure.scala @@ -9,8 +9,8 @@ class A { object Test { def main(args: Array[String]): Unit = { - classOf[A].getMethods.toList.filter(_.getName.startsWith("foo")).foreach { m => - assert(m.getGenericReturnType == Void.TYPE, s"Method does not return void: `${m}`") + classOf[A].getMethods.toList.filter(_.nn.getName.startsWith("foo")).foreach { m => + assert(m.nn.getGenericReturnType == Void.TYPE, s"Method does not return void: `${m}`") } } -} \ No newline at end of file +} diff --git a/tests/run/value-class-extractor-2.scala b/tests/run/value-class-extractor-2.scala index 5850d42f037e..22dc9952b357 100644 --- a/tests/run/value-class-extractor-2.scala +++ b/tests/run/value-class-extractor-2.scala @@ -43,7 +43,7 @@ object ValueOpt { // 69: astore 5 // 71: aload 5 // 73: areturn - def unapply(x: Any): Opt[String] = x match { + def unapply(x: Any): Opt[String|Null] = x match { case _: String => Opt("String") case _: List[_] => Opt("List") case _: Int => Opt("Int") diff --git a/tests/run/value-class-partial-func-depmet.scala b/tests/run/value-class-partial-func-depmet.scala index f8d2a16e73c5..4ba56adba10c 100644 --- a/tests/run/value-class-partial-func-depmet.scala +++ b/tests/run/value-class-partial-func-depmet.scala @@ -1,5 +1,8 @@ class C -class A { class C } +class A { + type C = C1|Null + class C1 +} object Test { def main(args: Array[String]): Unit = {