diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index efde897cd3e3..9f1be393d63a 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -11,6 +11,7 @@ import typer.{FrontEnd, Typer, ImportInfo, RefChecks} import reporting.{Reporter, ConsoleReporter} import Phases.Phase import transform._ +import transform.phantom._ import util.FreshNameCreator import transform.TreeTransforms.{TreeTransform, TreeTransformer} import core.DenotTransformers.DenotTransformer @@ -73,7 +74,10 @@ class Compiler { new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings new ResolveSuper, // Implement super accessors and add forwarders to trait methods new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives - new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. + new ArrayConstructors, // Intercept creation of (non-generic) arrays and intrinsify. + new PhantomTermEval), // Extracts the evaluation of phantom arguments placing them before the call. + List(new PhantomTermErasure), // Erases phantom parameters and arguments + List(new PhantomTypeErasure), // Erases phantom types to ErasedPhantom List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations @@ -90,6 +94,7 @@ class Compiler { List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments // Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here new ElimStaticThis, // Replace `this` references to static objects by global identifiers + new PhantomVals, // Remove ValDefs of phantom type new Flatten, // Lift all inner classes to package scope new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group List(new MoveStatics, // Move static methods to companion classes diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index b5be894402fa..bbef332008e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -626,16 +626,6 @@ object desugar { tree } - /** EmptyTree in lower bound ==> Nothing - * EmptyTree in upper bounds ==> Any - */ - def typeBoundsTree(tree: TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = { - val TypeBoundsTree(lo, hi) = tree - val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo - val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi - cpy.TypeBoundsTree(tree)(lo1, hi1) - } - /** Make closure corresponding to function. * params => body * ==> diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4d4350f98651..943b5297caa8 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -60,14 +60,14 @@ class Definitions { private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) + private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) = + scope.enter(newSymbol(cls, name, flags, typeBounds)) - private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) + private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) = + enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope, typeBounds) private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") = - enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) + enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope, TypeBounds.empty) // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only // implemented in Dotty and not in Scala 2. @@ -89,7 +89,7 @@ class Definitions { newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered } - /** The trait FunctionN or ImplicitFunctionN, for some N + /** The trait FunctionN, ImplicitFunctionN, PhantomFunctionM, ImplicitPhantomFunctionM, for some N * @param name The name of the trait to be created * * FunctionN traits follow this template: @@ -107,30 +107,45 @@ class Definitions { * trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] { * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R * } + * + * PhantomFunctionM traits follow this template: + * + * trait PhantomFunctionM[T0,...T{N-1}, R] extends Object { + * def apply($x0: T0, ..., $x{N_1}: T{N-1}): R + * } + * + * where M represents the phantomicity of all Ti. + * + * ImplicitPhantomFunctionM traits follow this template: + * + * trait ImplicitPhantomFunctionM[T0,...,T{N-1}, R] extends Object with PhantomFunctionM[T0,...,T{N-1}, R] { + * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R + * } + * + * where M represents the phantomicity of all Ti. + * */ private def newFunctionNTrait(name: TypeName) = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope - val arity = name.functionArity + val phantomicity = name.phantomicity + val arity = phantomicity.arity val argParams = for (i <- List.range(0, arity)) yield - enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls) - val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls) + enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls, phantomicity.tParamBounds(i)).typeRef + val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls, phantomicity.tParamBounds(arity)).typeRef val (methodType, parentTraits) = - if (name.startsWith(tpnme.ImplicitFunction)) { - val superTrait = - FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) - (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) + if (name.isImplicitFunction) { + val superTrait = FunctionType(phantomicity, isImplicit = false) + val appliedSuperTrait = superTrait.appliedTo(argParams ::: resParam :: Nil) + (ImplicitMethodType, ctx.normalizeToClassRefs(appliedSuperTrait :: Nil, cls, decls)) } else (MethodType, Nil) - val applyMeth = - decls.enter( - newMethod(cls, nme.apply, - methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) - denot.info = - ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls) + + decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred)) + denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls) } } newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer) @@ -149,17 +164,18 @@ class Definitions { } private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, - resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { + resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags, + bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = { val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) - val tparamBounds = tparamNames map (_ => TypeBounds.empty) + val tparamBounds = tparamNames map bounds val ptype = PolyType(tparamNames, tparamNames.map(alwaysZero))(_ => tparamBounds, resultTypeFn) enterMethod(cls, name, ptype, flags) } - private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet, bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = enterPolyMethod(cls, name, 1, resultTypeFn, flags) - private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = + private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet, bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = enterPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = { @@ -639,7 +655,7 @@ class Definitions { object FunctionOf { def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) = - FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil) + FunctionType(Phantomicity(args :+ resultType), isImplicit).appliedTo(args ::: resultType :: Nil) def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol if (isFunctionClass(tsym)) { @@ -697,11 +713,18 @@ class Definitions { lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2) lazy val ProductNType = mkArityArray("scala.Product", MaxTupleArity, 0) - def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context) = + def FunctionClass(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): Symbol = if (isImplicit) ctx.requiredClass("scala.ImplicitFunction" + n.toString) else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n) else ctx.requiredClass("scala.Function" + n.toString) + def FunctionClass(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): Symbol = { + if (phantomicity.hasPhantoms) { + val prefix = if (isImplicit) "scala.ImplicitPhantomFunction" else "scala.PhantomFunction" + ctx.requiredClass(prefix + phantomicity.encodedString) + } else FunctionClass(phantomicity.arity, isImplicit) + } + lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply) def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol @@ -709,6 +732,11 @@ class Definitions { if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n) else FunctionClass(n, isImplicit).typeRef + def FunctionType(phantomicity: Phantomicity, isImplicit: Boolean)(implicit ctx: Context): TypeRef = { + if (phantomicity.hasPhantoms) FunctionClass(phantomicity, isImplicit).typeRef + else FunctionType(phantomicity.arity, isImplicit) + } + private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet private lazy val ProductTypes: Set[TypeRef] = ProductNType.toSet @@ -734,23 +762,37 @@ class Definitions { /** Is a function class. * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 0 + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M */ def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction /** Is an implicit function class. * - ImplicitFunctionN for N >= 0 + * - ImplicitPhantomFunctionN for a valid M */ def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction + /** Is a phantom function class. + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M + */ + def isPhantomFunctionClass(cls: Symbol) = scalaClassName(cls).isPhantomFunction + /** Is a class that will be erased to FunctionXXL * - FunctionN for N >= 22 * - ImplicitFunctionN for N >= 22 + * - PhantomFunctionM for N >= 22, where N is the number of non phantoms in M + * - ImplicitPhantomFunctionM for N >= 22, where N is the number of non phantoms in M */ - def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity + def isXXLFunctionClass(cls: Symbol) = + scalaClassName(cls).phantomicity.erasedArity > MaxImplementedFunctionArity /** Is a synthetic function class * - FunctionN for N > 22 * - ImplicitFunctionN for N >= 0 + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M */ def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction @@ -759,31 +801,42 @@ class Definitions { def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) /** Returns the erased class of the function class `cls` - * - FunctionN for N > 22 becomes FunctionXXL * - FunctionN for 22 > N >= 0 remains as FunctionN - * - ImplicitFunctionN for N > 22 becomes FunctionXXL + * - FunctionN for N > 22 becomes FunctionXXL * - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN + * - ImplicitFunctionN for N > 22 becomes FunctionXXL * - anything else becomes a NoSymbol */ def erasedFunctionClass(cls: Symbol): Symbol = { - val arity = scalaClassName(cls).functionArity - if (arity > 22) defn.FunctionXXLClass - else if (arity >= 0) defn.FunctionClass(arity) - else NoSymbol + val phantomicity = scalaClassName(cls).phantomicity + if (!phantomicity.isValid) NoSymbol + else if (phantomicity.erasedArity > 22) defn.FunctionXXLClass + else defn.FunctionClass(phantomicity.erasedArity) + } + + /** Returns the erased class of the function class `cls` + * - PhantomFunctionM becomes FunctionN where N is the number of non phantoms in M + * - ImplicitPhantomFunctionM becomes ImplicitFunctionN where N is the number of non phantoms in M + * - cls otherwise + */ + def erasedPhantomsFunctionClass(cls: Symbol): Symbol = { + val phantomicity = scalaClassName(cls).phantomicity + if (!phantomicity.isValid) cls + else defn.FunctionClass(phantomicity.erasedArity, cls.name.isImplicitFunction) } /** Returns the erased type of the function class `cls` - * - FunctionN for N > 22 becomes FunctionXXL * - FunctionN for 22 > N >= 0 remains as FunctionN - * - ImplicitFunctionN for N > 22 becomes FunctionXXL + * - FunctionN for N > 22 becomes FunctionXXL * - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN + * - ImplicitFunctionN for N > 22 becomes FunctionXXL * - anything else becomes a NoType */ def erasedFunctionType(cls: Symbol): Type = { - val arity = scalaClassName(cls).functionArity - if (arity > 22) defn.FunctionXXLType - else if (arity >= 0) defn.FunctionType(arity) - else NoType + val phantomicity = scalaClassName(cls).phantomicity + if (!phantomicity.isValid) NoType + else if (phantomicity.erasedArity > 22) defn.FunctionXXLType + else defn.FunctionType(phantomicity.erasedArity) } val predefClassNames: Set[Name] = @@ -815,7 +868,7 @@ class Definitions { lazy val UnqualifiedOwnerTypes: Set[NamedType] = RootImportTypes.toSet[NamedType] ++ RootImportTypes.map(_.symbol.moduleClass.typeRef) - lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) + lazy val NotRuntimeClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) /** Classes that are known not to have an initializer irrespective of * whether NoInits is set. Note: FunctionXXLClass is in this set @@ -827,7 +880,10 @@ class Definitions { * trait gets screwed up. Therefore, it is mandatory that FunctionXXL * is treated as a NoInit trait. */ - lazy val NoInitClasses = PhantomClasses + FunctionXXLClass + private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass + + def isNoInitClass(cls: Symbol): Boolean = + cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls) def isPolymorphicAfterErasure(sym: Symbol) = (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) @@ -856,7 +912,7 @@ class Definitions { def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) val sym = tp.dealias.typeSymbol - arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol) + arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(sym.name.phantomicity, sym.name.isImplicitFunction).typeSymbol) } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 @@ -938,16 +994,17 @@ class Definitions { val newDecls = new MutableScope(oldDecls) { override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { val res = super.lookupEntry(name) - if (res == null && name.isTypeName && name.isSyntheticFunction) + if (res ne null) res + else if (name.isTypeName && name.isSyntheticFunction) newScopeEntry(newFunctionNTrait(name.asTypeName)) - else res + else null } } ScalaPackageClass.info = oldInfo.derivedClassInfo(decls = newDecls) } /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - lazy val syntheticScalaClasses = List( + private lazy val syntheticScalaClasses = List( AnyClass, AnyRefAlias, RepeatedParamClass, @@ -956,14 +1013,15 @@ class Definitions { NullClass, NothingClass, SingletonClass, - EqualsPatternClass) + EqualsPatternClass, + PhantomClass) lazy val syntheticCoreClasses = syntheticScalaClasses ++ List( EmptyPackageVal, OpsPackageClass) /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) + private lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet @@ -985,4 +1043,46 @@ class Definitions { _isInitialized = true } } + + // ----- Phantoms --------------------------------------------------------- + + lazy val PhantomClass: ClassSymbol = { + val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, Abstract, List(AnyType))) + + val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil) + val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef)) + + val tparamNames = tpnme.syntheticTypeParamNames(1) + val ptype = PolyType(tparamNames, List(0))(_ => TypeBounds(nothing.typeRef, any.typeRef) :: Nil, PolyParam(_, 0)) + newSymbol(cls, nme.assume_, Protected | Final | Method, ptype).entered + + cls + } + + def isPhantomAnyClass(sym: Symbol)(implicit ctx: Context): Boolean = + sym.exists && (sym.owner eq PhantomClass) && sym.name == tpnme.Any + + def isPhantomNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = + sym.exists && (sym.owner eq PhantomClass) && sym.name == tpnme.Nothing + + def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean = + sym.exists && (sym.owner eq PhantomClass) && sym.name == nme.assume_ + + def topOf(tp: Type)(implicit ctx: Context): Type = { + val lattice = tp.phantomLatticeClass + if (lattice.exists) lattice.select(tpnme.Any) + else defn.AnyType + } + + def bottomOf(tp: Type)(implicit ctx: Context): Type = { + val lattice = tp.phantomLatticeClass + if (lattice.exists) lattice.select(tpnme.Nothing) + else defn.NothingType + } + + lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") + def ErasedPhantomType = ErasedPhantomClass.typeRef + + lazy val ErasedPhantomLatticeClass = ctx.requiredClass("dotty.runtime.ErasedPhantomLattice") + def ErasedPhantomLatticeType = ErasedPhantomLatticeClass.typeRef } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index ea905c19f50a..42f99fdc1fb9 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -232,35 +232,63 @@ object NameOps { } } - /** Is a synthetic function name + /** Return the function arity * - N for FunctionN * - N for ImplicitFunctionN - * - (-1) otherwise + * - N for PhantomFunctionM where N is the length of M + * - N for ImplicitPhantomFunctionM where N is the length of M + * - (-1) otherwise */ - def functionArity: Int = - functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction) + def functionArity(implicit ctx: Context): Int = phantomicity.arity + + /** Checks and returns the phantomicity of the function */ + def phantomicity(implicit ctx: Context): Phantomicity = { + val arity = functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction) + if (arity >= 0) Phantomicity.noPhantoms(arity) + else { + val phantomicity = phantomFunctionPhantomicity(tpnme.PhantomFunction) + if (phantomicity.isValid) phantomicity + else phantomFunctionPhantomicity(tpnme.ImplicitPhantomFunction) + } + } /** Is a function name * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 0 + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M * - false otherwise */ - def isFunction: Boolean = functionArity >= 0 + def isFunction(implicit ctx: Context): Boolean = functionArity >= 0 /** Is a implicit function name * - ImplicitFunctionN for N >= 0 + * - ImplicitPhantomFunctionM for a valid M + * - false otherwise + */ + def isImplicitFunction: Boolean = { + functionArityFor(tpnme.ImplicitFunction) >= 0 || + phantomFunctionPhantomicity(tpnme.ImplicitPhantomFunction).isValid + } + + /** Is a phantom function name + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M * - false otherwise */ - def isImplicitFunction: Boolean = functionArityFor(tpnme.ImplicitFunction) >= 0 + def isPhantomFunction(implicit ctx: Context): Boolean = phantomicity.hasPhantoms /** Is a synthetic function name * - FunctionN for N > 22 * - ImplicitFunctionN for N >= 0 + * - PhantomFunctionM for a valid M + * - ImplicitPhantomFunctionM for a valid M * - false otherwise */ - def isSyntheticFunction: Boolean = { - functionArityFor(tpnme.Function) > MaxImplementedFunctionArity || - functionArityFor(tpnme.ImplicitFunction) >= 0 + def isSyntheticFunction(implicit ctx: Context): Boolean = { + val p = phantomicity + if (name.startsWith(tpnme.Function)) p.arity > MaxImplementedFunctionArity + else p.isValid } /** Parsed function arity for function with some specific prefix */ @@ -271,6 +299,12 @@ object NameOps { else -1 } + /** Parsed function phantomicity for function with some specific prefix */ + private def phantomFunctionPhantomicity(prefix: Name): Phantomicity = { + lazy val p = Phantomicity.from(name.toString.substring(prefix.length)) + if (name.startsWith(prefix) && p.isValid) p + else Phantomicity.invalid + } /** The number of hops specified in an outer-select name */ def outerSelectHops: Int = { diff --git a/compiler/src/dotty/tools/dotc/core/Phantomicity.scala b/compiler/src/dotty/tools/dotc/core/Phantomicity.scala new file mode 100644 index 000000000000..220547298c2c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/Phantomicity.scala @@ -0,0 +1,77 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Names._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ + +/** Suffix encoding M of `PhantomFunctionM` and `ImplicitPhantomFunctionM` + * + * `FunctionN` and `ImplicitFunctionN` can also be encoded as + * `PhantomFunctionM` and `ImplicitPhantomFunctionM` where `M` has `N + 1` `O`s + */ +class Phantomicity private (val encodedNames: Array[TermName]) extends AnyVal { + import Phantomicity._ + + /** If has phantom parameters or return type */ + def hasPhantoms: Boolean = encodedNames.exists(_ != scalaLattice) + + /** If all parameters and return type are phantoms */ + def allPhantoms: Boolean = encodedNames.forall(_ != scalaLattice) + + /** If the return type is a phantom */ + def returnsPhantom: Boolean = encodedNames.last != scalaLattice + + /** Arity of this function. Including phantom parameters. */ + def arity: Int = if (isValid) encodedNames.length - 1 else -1 + + /** Erased arity of this function. Without phantom parameters. */ + def erasedArity: Int = + if (isValid) encodedNames.init.count(_ == scalaLattice) + else -1 + + def tParamBounds(i: Int)(implicit ctx: Context): TypeBounds = { + val latticeName = encodedNames(i).decode + if (latticeName == scalaLattice) { + TypeBounds.empty + } else { + val lattice = staticRefOf(latticeName).info + TypeBounds(lattice.select(tpnme.Nothing), lattice.select(tpnme.Any)) + } + } + + /** Is a valid phantomicity */ + def isValid: Boolean = encodedNames ne invalid.encodedNames + + /** Encoded suffix of the function name */ + def encodedString: String = "$$" + encodedNames.mkString(separator.toString) + +} + +object Phantomicity { + private val separator = '_' + private val scalaLattice = "scala".toTermName + + lazy val invalid: Phantomicity = new Phantomicity(Array.empty) + + def apply(types: List[Type])(implicit ctx: Context): Phantomicity = { + def typeToString(tp: Type) = { + val lattice = tp.phantomLatticeClass + if (lattice.exists) lattice.termSymbol.fullName.encode.asTermName + else scalaLattice + } + val encodedStrings = types.iterator.map(typeToString).toArray + new Phantomicity(encodedStrings) + } + + def from(str: String): Phantomicity = + new Phantomicity(str.substring(2).split(separator).map(_.toTermName)) + + def noPhantoms(arity: Int)(implicit ctx: Context): Phantomicity = apply(List.fill(arity + 1)(defn.AnyType)) + + private def staticRefOf(name: Name)(implicit ctx: Context) = + if (name.contains('.')) ctx.base.staticRef(name) + else defn.EmptyPackageClass.info.decl(name) +} diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 44608296a2b1..e4f757f1c9b8 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -13,6 +13,7 @@ import config.Printers.config import scala.collection.mutable.{ListBuffer, ArrayBuffer} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} import dotty.tools.dotc.transform._ +import dotty.tools.dotc.transform.phantom._ import Periods._ import typer.{FrontEnd, RefChecks} import ast.tpd @@ -309,6 +310,7 @@ object Phases { private var myPeriod: Period = Periods.InvalidPeriod private var myBase: ContextBase = null + private var myErasedPhantomTerms = false private var myErasedTypes = false private var myFlatClasses = false private var myRefChecked = false @@ -326,6 +328,7 @@ object Phases { def start = myPeriod.firstPhaseId def end = myPeriod.lastPhaseId + final def erasedPhantomTerms = myErasedPhantomTerms // Phase is after phantom terms final def erasedTypes = myErasedTypes // Phase is after erasure final def flatClasses = myFlatClasses // Phase is after flatten final def refChecked = myRefChecked // Phase is after RefChecks @@ -337,6 +340,7 @@ object Phases { assert(myPeriod == Periods.InvalidPeriod, s"phase $this has already been used once; cannot be reused") myBase = base myPeriod = Period(NoRunId, start, end) + myErasedPhantomTerms = prev.getClass == classOf[PhantomTermErasure] || prev.erasedPhantomTerms myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 5b7dc3d1d5b2..d2a5bb0aeaee 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -185,6 +185,8 @@ object StdNames { final val ExprApi: N = "ExprApi" final val Function: N = "Function" final val ImplicitFunction: N = "ImplicitFunction" + final val PhantomFunction: N = "PhantomFunction" + final val ImplicitPhantomFunction: N = "ImplicitPhantomFunction" final val Mirror: N = "Mirror" final val Nothing: N = "Nothing" final val Null: N = "Null" @@ -230,6 +232,8 @@ object StdNames { final val SourceFileATTR: N = "SourceFile" final val SyntheticATTR: N = "Synthetic" + final val Phantom: N = "Phantom" + // ----- Term names ----------------------------------------- // Compiler-internal diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index db96463e0813..e2cb52728efc 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -488,8 +488,8 @@ object SymDenotations { def isNumericValueClass(implicit ctx: Context) = maybeOwner == defn.ScalaPackageClass && defn.ScalaNumericValueClasses().contains(symbol) - /** Is symbol a phantom class for which no runtime representation exists? */ - def isPhantomClass(implicit ctx: Context) = defn.PhantomClasses contains symbol + /** Is symbol a class for which no runtime representation exists? */ + def isNotRuntimeClass(implicit ctx: Context) = defn.NotRuntimeClasses contains symbol /** Is this symbol a class representing a refinement? These classes * are used only temporarily in Typer and Unpickler as an intermediate diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 57dde328858f..60da8b2b8eee 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -12,6 +12,7 @@ import config.Printers.{typr, constr, subtyping, noPrinter} import TypeErasure.{erasedLub, erasedGlb} import TypeApplications._ import scala.util.control.NonFatal +import scala.annotation.tailrec /** Provides methods to compare types. */ @@ -550,8 +551,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false } - (tp1.symbol eq NothingClass) && tp2.isValueTypeOrLambda || - (tp1.symbol eq NullClass) && isNullable(tp2) + def isPhantom(tp: Type): Boolean = tp.widenDealias match { + case tp: TypeRef => defn.isPhantomAnyClass(tp.symbol) + case tp: RefinedOrRecType => isPhantom(tp.parent) + case tp: AndOrType => isPhantom(tp.tp1) + case _ => false + } + if (tp1.symbol eq NothingClass) tp2.isValueTypeOrLambda && !isPhantom(tp2) + else if (tp1.symbol eq NullClass) isNullable(tp2) && !isPhantom(tp2) + else if (defn.isPhantomNothingClass(tp1.symbol)) tp2.isValueTypeOrLambda && (tp1.phantomLatticeClass == tp2.phantomLatticeClass) + else false } case tp1: SingletonType => /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/ diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 3d290632080b..e7cf8ec3c007 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -15,6 +15,8 @@ import util.{SimpleMap, Property} import collection.mutable import ast.tpd._ +import scala.annotation.tailrec + trait TypeOps { this: Context => // TODO: Make standalone object. /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec @@ -199,7 +201,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } /** The minimal set of classes in `cs` which derive all other classes in `cs` */ - def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match { + @tailrec def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = cs match { case c :: rest => val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu if (cs == c.baseClasses) accu1 else dominators(rest, accu1) @@ -255,7 +257,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else tp.baseTypeWithArgs(cls) base.mapReduceOr(identity)(mergeRefined) } - doms.map(baseTp).reduceLeft(AndType.apply) + if (doms.isEmpty) new ErrorType("no parents in common") // This can happen in the union of Any with PhantomAny + else doms.map(baseTp).reduceLeft(AndType.apply) } } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c80107f93858..4c1af00ed497 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -169,7 +169,16 @@ object Types { case _ => false } - cls == defn.AnyClass || loop(this) + loop(this) + } + + final def isPhantom(implicit ctx: Context): Boolean = phantomLatticeClass.exists + + final def phantomLatticeClass(implicit ctx: Context): Type = this match { + case tp: ClassInfo if tp.classSymbol.owner eq defn.PhantomClass => tp.prefix + case tp: TypeProxy => tp.superType.phantomLatticeClass + case tp: AndOrType => tp.tp1.phantomLatticeClass + case _ => NoType } /** Is this type guaranteed not to have `null` as a value? diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 6ea168b99241..0c1535ff5be2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -56,6 +56,14 @@ public enum ErrorMessageID { CyclicReferenceInvolvingID, CyclicReferenceInvolvingImplicitID, SuperQualMustBeParentID, + ErasedPhantomsSignatureCollisionID, + PhantomInheritanceID, + PhantomMixedBoundsID, + PhantomCrossedMixedBoundsID, + MatchPhantomID, + MatchOnPhantomID, + IfElsePhantomID, + PhantomIsInObjectID, ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 6fa05664688e..a40cedc3850b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -10,6 +10,7 @@ import Symbols._ import Names._ import NameOps._ import Types._ +import Flags._ import util.SourcePosition import config.Settings.Setting import interfaces.Diagnostic.{ERROR, INFO, WARNING} @@ -133,7 +134,7 @@ object messages { } case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) - extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { + extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { val kind = "Syntax" val msg = hl"""|The ${"catch"} block does not contain a valid expression, try @@ -1209,4 +1210,81 @@ object messages { |${parents.mkString(" - ", "\n - ", "")} |""".stripMargin } + + case class ErasedPhantomsSignatureCollision(decls: Iterable[Symbol], erased: (Name, Type))(implicit ctx: Context) + extends Message(ErasedPhantomsSignatureCollisionID) { + val kind = "Phantom restriction" + val msg = em"After phantom erasure methods $methodsString will have the same signature: ${erased._1}${erased._2}" + + private def methodsString = decls.map(decl => em"${decl.name}${decl.info}").mkString(", ") + + val explanation = + hl"""|Phantom erasure removes all phantom parameters/arguments from methods and functions. + |""".stripMargin + } + + case class PhantomInheritance(cdef: tpd.TypeDef)(implicit ctx: Context) + extends Message(PhantomInheritanceID) { + val kind = "Phantom restriction" + val msg = perfix + " cannot extend both Any and PhantomAny." + + def perfix = + if (cdef.symbol.flags.is(Flags.Trait)) "A trait" + else if (cdef.symbol.flags.is(Flags.Abstract)) "An abstract class" + else "A class" + + val explanation = + hl"""|""".stripMargin + } + + case class PhantomMixedBounds(op: untpd.Ident)(implicit ctx: Context) + extends Message(PhantomMixedBoundsID) { + val kind = "Phantom restriction" + val msg = hl"Can not mix types of ${"Any"} and ${"Phantom.Any"} with ${op.show}." + + val explanation = + hl"""|""".stripMargin + } + + case class PhantomCrossedMixedBounds(lo: untpd.Tree, hi: untpd.Tree)(implicit ctx: Context) + extends Message(PhantomCrossedMixedBoundsID) { + val kind = "Phantom restriction" + val msg = hl"Type can not be bounded at the same time by types in the ${"Any"} and ${"Phantom.Any"} latices." + + val explanation = + hl"""|""".stripMargin + } + + case class MatchPhantom()(implicit ctx: Context) extends Message(MatchPhantomID) { + val kind = "Phantom restriction" + val msg = "Pattern matches cannot return phantom and non phantoms" + + val explanation = + hl"""|""".stripMargin + } + + + case class MatchOnPhantom()(implicit ctx: Context) extends Message(MatchOnPhantomID) { + val kind = "Phantom restriction" + val msg = "Cannot pattern match on phantoms" + + val explanation = + hl"""|""".stripMargin + } + + case class IfElsePhantom()(implicit ctx: Context) extends Message(IfElsePhantomID) { + val kind = "Phantom restriction" + val msg = "Cannot yield a phantom type in one of the if branches and not in the other one." + + val explanation = + hl"""|""".stripMargin + } + + case class PhantomIsInObject()(implicit ctx: Context) extends Message(PhantomIsInObjectID) { + val kind = "Phantom restriction" + val msg = s"Only ${"object"} can extend ${"scala.Phantom"}" + + val explanation = + hl"""|""".stripMargin + } } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index ae325950958c..e388889d9f2b 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -89,7 +89,7 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati case _ => val elemType = tree.tpe.elemType var elemClass = elemType.classSymbol - if (defn.PhantomClasses contains elemClass) elemClass = defn.ObjectClass + if (defn.NotRuntimeClasses contains elemClass) elemClass = defn.ObjectClass ref(defn.DottyArraysModule) .select(nme.seqToArray) .appliedToType(elemType) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index f9c7a8e1e09c..635bd8b022d3 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -615,8 +615,17 @@ object Erasure extends TypeTestsCasts{ s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}") val newOverridden = oldSymbol.denot.allOverriddenSymbols.toSet // TODO: clarify new <-> old in a comment; symbols are swapped here val oldOverridden = newSymbol.allOverriddenSymbols(beforeCtx).toSet // TODO: can we find a more efficient impl? newOverridden does not have to be a set! + + def isPhantomFunctionApply(sym: Symbol) = { + val owner = sym.owner + defn.isPhantomFunctionClass(owner) && + !owner.name.phantomicity.allPhantoms && // as it is already erased to ():Object + ctx.owner.derivesFrom(defn.erasedPhantomsFunctionClass(owner)) + } + def stillInBaseClass(sym: Symbol) = ctx.owner derivesFrom sym.owner - val neededBridges = (oldOverridden -- newOverridden).filter(stillInBaseClass) + + val neededBridges = (oldOverridden -- newOverridden).filter(s => stillInBaseClass(s) || isPhantomFunctionApply(s)) var minimalSet = Set[Symbol]() // compute minimal set of bridges that are needed: diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index fd4370d3e319..f33f90ee4ce4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -173,9 +173,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { case Some(call) => - if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil + if (defn.NotRuntimeClasses.contains(baseCls)) Nil else call :: Nil case None => - if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil + if (defn.isNoInitClass(baseCls)) Nil else { //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil diff --git a/compiler/src/dotty/tools/dotc/transform/PhantomVals.scala b/compiler/src/dotty/tools/dotc/transform/PhantomVals.scala new file mode 100644 index 000000000000..46c9144ec177 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PhantomVals.scala @@ -0,0 +1,59 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers._ +import Phases.Phase +import Contexts.Context +import Symbols._ +import Constants._ +import TreeTransforms._ +import Flags._ + +/** Erases all ValDefs of phantom type, + * except for `lazy val` as the field are not yet generated. + * + * def x(): ErasedPhantom = e + * --> def x(): ErasedPhantom = null + * + * + * x = e --> e where type of x is ErasedPhantom + * + * Filed in class + * val x: ErasedPhantom --> EmptyTree + * + * Field in method + * val x: ErasedPhantom = e --> e + */ + class PhantomVals extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "phantomVals" + + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { + // TODO + } + + override def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + if (sym.is(Lazy) || !(tree.tpt.tpe =:= defn.ErasedPhantomType)) tree + else if (sym.is(Private)) EmptyTree + else tree.rhs + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val sym = tree.symbol + if (sym.isGetter && !sym.is(Lazy) && (tree.tpt.tpe =:= defn.ErasedPhantomType)) { + cpy.DefDef(tree)(rhs = Literal(Constant(null)).withType(defn.ErasedPhantomType)) + } else tree + } + + override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = { + val lhsSym = tree.lhs.symbol + if (!lhsSym.is(Lazy) && tree.rhs.tpe =:= defn.ErasedPhantomType) tree.rhs + else tree + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/TreeGen.scala b/compiler/src/dotty/tools/dotc/transform/TreeGen.scala index 7e507d905894..a6dfdebc3cf4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeGen.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeGen.scala @@ -14,7 +14,7 @@ object TreeGen { def wrapArrayMethodName(elemtp: Type)(implicit ctx: Context): TermName = { val elemCls = elemtp.classSymbol if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name) - else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isPhantomClass) nme.wrapRefArray + else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isNotRuntimeClass) nme.wrapRefArray else nme.genericWrapArray } diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermErasure.scala new file mode 100644 index 000000000000..ca7ee85fb076 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermErasure.scala @@ -0,0 +1,59 @@ +package dotty.tools.dotc.transform.phantom + +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.DenotTransformers._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} + +class PhantomTermErasure extends MiniPhaseTransform with InfoTransformer { + import tpd._ + + override def phaseName: String = "phantomTermErasure" + + /** Check what the phase achieves, to be called at any point after it is finished. */ + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { + def assertNotPhantom(tree: Tree): Unit = + assert(!tree.tpe.isPhantom, "All phantom type values should be erased in " + tree) + tree match { + case Apply(_, args) => args.foreach(assertNotPhantom) + case DefDef(_, _, vparamss, tpt, _) => vparamss.foreach(_.foreach(assertNotPhantom)) + case _ => + } + } + + /* Tree transform */ + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + cpy.Apply(tree)(tree.fun, tree.args.filter(!_.tpe.isPhantom)) + + override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = + cpy.DefDef(ddef)(vparamss = ddef.vparamss.map(_.filter(!_.tpt.typeOpt.isPhantom))) + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.symbol.is(Param) && tree.tpe.isPhantom) Literal(Constant(null)).withType(tree.tpe) else tree + + /* Symbol transform */ + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = erasedPhantomParameters(tp) + + /* private methods */ + + private def erasedPhantomParameters(tp: Type)(implicit ctx: Context): Type = tp match { + case tp: JavaMethodType => tp + case tp: MethodType => + val methodType = if (tp.isImplicit) ImplicitMethodType else MethodType + val (erasedParamNames, erasedParamTypes) = + tp.paramNames.zip(tp.paramTypes).filterNot(_._2.isPhantom).unzip + val erasedReturnType = erasedPhantomParameters(tp.resultType) + methodType(erasedParamNames, erasedParamTypes, erasedReturnType) + case tp: PolyType => + val erasedReturnType = erasedPhantomParameters(tp.resultType) + tp.derivedPolyType(tp.paramNames, tp.paramBounds, erasedReturnType) + case _ => tp + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermEval.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermEval.scala new file mode 100644 index 000000000000..efa01b0b97d5 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTermEval.scala @@ -0,0 +1,50 @@ +package dotty.tools.dotc.transform.phantom + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} + +class PhantomTermEval extends MiniPhaseTransform { + import tpd._ + + override def phaseName: String = "phantomTermEval" + + /** Check what the phase achieves, to be called at any point after it is finished. */ + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { + // TODO + } + + /* Tree transform */ + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = tree.tpe match { + case _: MethodType => tree + case _ if tree.args.forall(!_.tpe.isPhantom) => tree + case _ => + val argsVals = tree.args.map(toSynthVal) + + def evalArgsInBlock() = Block(argsVals, cpy.Apply(tree)(tree.fun, refs(argsVals))) + def evalFunAndArgsInBlock(fun: Select) = { + val qualVal = toSynthVal(fun.qualifier) + val app = cpy.Apply(tree)(Select(ref(qualVal.symbol), fun.name), refs(argsVals)) + Block(qualVal :: argsVals, app) + } + + tree.fun match { + case fun: Select => + if (fun.qualifier.isInstanceOf[New]) evalArgsInBlock() + else evalFunAndArgsInBlock(fun) + case _ => evalArgsInBlock() + } + } + + /* private methods */ + + private def toSynthVal(t: Tree)(implicit ctx: Context) = + SyntheticValDef(ctx.freshName("ev$").toTermName, t) + + private def refs(vals: List[Tree])(implicit ctx: Context) = + vals.map(x => ref(x.symbol)) + +} diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala new file mode 100644 index 000000000000..4f549ecfa937 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala @@ -0,0 +1,59 @@ +package dotty.tools.dotc.transform.phantom + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.DenotTransformers._ +import dotty.tools.dotc.core.Phases._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} + +class PhantomTypeErasure extends MiniPhaseTransform with InfoTransformer { + + import tpd._ + + override def phaseName: String = "phantomTypeErasure" + + /** List of names of phases that should precede this phase */ + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[PhantomTermErasure]) + + /** Check what the phase achieves, to be called at any point after it is finished. */ + override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = { + assert(!tree.tpe.isPhantom, tree.tpe + " should be erased in " + tree) + } + + /* Tree transform */ + + override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree = { + val newTpe = erasePhantomAnyType(tree.tpe) + if (newTpe =:= tree.tpe) tree else TypeTree(newTpe) + } + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = + if (defn.isPhantomAssume(tree.fun.symbol)) Literal(Constant(null)).withType(defn.ErasedPhantomType) else tree + + /* Symbol transform */ + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = erasePhantomAnyType(tp) + + /* private methods */ + + private def erasePhantomAnyType(tp: Type)(implicit ctx: Context): Type = { + val erasePhantomAnyTypeMap = new DeepTypeMap { + override def apply(tp: Type): Type = tp match { + case tp: TypeRef if defn.isPhantomAnyClass(tp.symbol) || defn.isPhantomNothingClass(tp.symbol) => + defn.ErasedPhantomType + case tp: TypeRef if tp.typeSymbol eq defn.PhantomClass => + defn.ErasedPhantomLatticeType + case tp: MethodType if tp.resultType.isPhantom => + // Erase return type to Object to match FunctionN erased return type + val methodType = if (tp.isImplicit) ImplicitMethodType else MethodType + methodType(tp.paramNames, tp.paramTypes, defn.ObjectType) + case _ => mapOver(tp) + } + } + erasePhantomAnyTypeMap(tp) + } + +} diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6d0fc08f9092..08c97d0f5616 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -318,7 +318,9 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case fntpe: MethodType => - if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) + def sameLengthAfterPhantomErasure = + ctx.phase.erasedPhantomTerms && sameLength(fntpe.paramTypes, args.filterNot(arg => arg.typeOpt.isPhantom)) + if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping|| sameLengthAfterPhantomErasure) fntpe.instantiate(args.tpes) else errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${fntpe.paramTypes.length}, found: ${args.length}", tree.pos) case t => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d4a9744e4faf..90b768d187c1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -661,6 +661,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val cond1 = typed(tree.cond, defn.BooleanType) val thenp1 = typed(tree.thenp, pt.notApplied) val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied) + if (thenp1.tpe.isPhantom ^ elsep1.tpe.isPhantom) + ctx.error(IfElsePhantom(), tree.pos) val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } @@ -681,9 +683,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) { - val funCls = defn.FunctionClass(args.length, tree.isInstanceOf[untpd.ImplicitFunction]) - typed(cpy.AppliedTypeTree(tree)( - untpd.TypeTree(funCls.typeRef), args :+ body), pt) + val phantomicity = Phantomicity((args :+ body).map(tr => typed(tr).tpe)) + val funTpe = defn.FunctionType(phantomicity, tree.isInstanceOf[untpd.ImplicitFunction]) + typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funTpe), args :+ body), pt) } else { val params = args.asInstanceOf[List[untpd.ValDef]] @@ -833,6 +835,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(desugar.makeCaseLambda(tree.cases, protoFormals.length, unchecked) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) + if (sel1.tpe.isPhantom) + ctx.error(MatchOnPhantom(), sel1.pos) val selType = widenForMatchSelector( fullyDefinedType(sel1.tpe, "pattern selector", tree.pos)) @@ -864,7 +868,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit accu(Set.empty, selType) } - cases mapconserve (typedCase(_, pt, selType, gadtSyms)) + val tpdCases = cases mapconserve (typedCase(_, pt, selType, gadtSyms)) + + val phantomBranches = tpdCases.count(_.body.tpe.isPhantom) + if (phantomBranches != 0 && phantomBranches != tpdCases.size) + ctx.error(MatchPhantom(), tpdCases.head.pos) + + tpdCases } /** Type a case. Overridden in ReTyper, that's why it's separate from @@ -1091,8 +1101,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] } + val tpt2 = adaptIfPhantomsFunction(tpt1, args1) // check that arguments conform to bounds is done in phase PostTyper - assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) + assignType(cpy.AppliedTypeTree(tree)(tpt2, args1), tpt2, args1) } } @@ -1120,10 +1131,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { - val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) + val TypeBoundsTree(lo, hi) = tree val lo1 = typed(lo) val hi1 = typed(hi) - val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) + + val lo2 = if (!lo1.isEmpty) lo1 else typed(untpd.TypeTree(defn.bottomOf(hi1.typeOpt))) + val hi2 = if (!hi1.isEmpty) hi1 else typed(untpd.TypeTree(defn.topOf(lo1.typeOpt))) + + val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo2, hi2), lo2, hi2) if (ctx.mode.is(Mode.Pattern)) { // Associate a pattern-bound type symbol with the wildcard. // The bounds of the type symbol can be constrained when comparing a pattern type @@ -1344,6 +1359,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cls, isRequired, cdef.pos) } + if (!cls.is(Module) && cls.classParents.exists(_.classSymbol eq defn.PhantomClass)) + ctx.error(PhantomIsInObject(), cdef.pos) + // check value class constraints checkDerivedValueClass(cls, body1) @@ -1648,8 +1666,32 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = typed(tree, pt)(ctx retractMode Mode.PatternOrType) - def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern? + def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = { + // todo: retract mode between Type and Pattern? + + /** Check that the are not mixed Any/Phantom.Any types in `&`, `|` and type bounds, + * this includes Phantom.Any of different universes. + */ + def checkedTops(tree: untpd.Tree): Set[Type] = { + def checkedTops2(tree1: untpd.Tree, tree2: untpd.Tree, msg: => Message, pos: Position): Set[Type] = { + val allTops = checkedTops(tree1) union checkedTops(tree2) + if (allTops.size > 1) + ctx.error(msg, tree.pos) + allTops + } + tree match { + case TypeBoundsTree(lo, hi) => + checkedTops2(lo, hi, PhantomCrossedMixedBounds(lo, hi), tree.pos) + case untpd.InfixOp(left, op, right) => + checkedTops2(left, right, PhantomCrossedMixedBounds(left, right), tree.pos) + case EmptyTree => Set.empty + case _ => Set(defn.topOf(tree.typeOpt)) + } + } + checkedTops(tree) + typed(tree, pt)(ctx addMode Mode.Type) + } def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(implicit ctx: Context): Tree = typed(tree, selType)(ctx addMode Mode.Pattern) @@ -2103,4 +2145,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } } + + private def adaptIfPhantomsFunction(tpt: tpd.Tree, args: List[tpd.Tree])(implicit ctx: Context): tpd.Tree = { + val sym = tpt.tpe.typeSymbol + if (!defn.isFunctionClass(sym) || !args.exists(_.tpe.isPhantom)) tpt + else { + val phantomicity = Phantomicity(args.map(_.tpe)) + val phantomFunction = Ident(defn.FunctionType(phantomicity, sym.name.isImplicitFunction)) + phantomFunction.setPosUnchecked(tpt.pos) + typed(phantomFunction, AnyTypeConstructorProto)(ctx.retractMode(Mode.Pattern)) + } + } + } diff --git a/library/src/dotty/runtime/ErasedPhantom.scala b/library/src/dotty/runtime/ErasedPhantom.scala new file mode 100644 index 000000000000..9a0dc6e50c8f --- /dev/null +++ b/library/src/dotty/runtime/ErasedPhantom.scala @@ -0,0 +1,3 @@ +package dotty.runtime + +final abstract class ErasedPhantom diff --git a/library/src/dotty/runtime/ErasedPhantomLattice.scala b/library/src/dotty/runtime/ErasedPhantomLattice.scala new file mode 100644 index 000000000000..e00c378a7ad6 --- /dev/null +++ b/library/src/dotty/runtime/ErasedPhantomLattice.scala @@ -0,0 +1,3 @@ +package dotty.runtime + +abstract class ErasedPhantomLattice diff --git a/library/src/scala/Phantom.scala b/library/src/scala/Phantom.scala new file mode 100644 index 000000000000..f8f52f22c93f --- /dev/null +++ b/library/src/scala/Phantom.scala @@ -0,0 +1,13 @@ +/* Defined synthetically +package scala + +trait Phantom { + /** Phantom.Any does not extend scala.Any */ + protected /*final*/ trait Any + + protected final abstract class Nothing extends Any + + protected final def assume[P >: this.Nothing <: this.Any]: P = + null.asInstanceOf[P] // This implementation matches the erased implementation +} +*/ diff --git a/tests/neg/phantom-AndOr.scala b/tests/neg/phantom-AndOr.scala new file mode 100644 index 000000000000..bb52563502b1 --- /dev/null +++ b/tests/neg/phantom-AndOr.scala @@ -0,0 +1,18 @@ + +class BooFunDef1 { + import Boo._ + + def fun1(b: BooAny | Any) = ??? // error + def fun2(b: BooAny | Any | Any) = ??? // error + def fun3(b: Any | BooAny | Any) = ??? // error + def fun4(b: BooAny | BooAny | Any) = ??? // error + + def fun5(b: BooAny & Any) = ??? // error + def fun6(b: Any & BooAny & Any) = ??? // error + def fun7(b: BooAny & Any & Any) = ??? // error + def fun8(b: Any & Any & BooAny) = ??? // error +} + +object Boo extends Phantom { + type BooAny = this.Any +} diff --git a/tests/neg/phantom-Eq.scala b/tests/neg/phantom-Eq.scala new file mode 100644 index 000000000000..db6d2744cb3e --- /dev/null +++ b/tests/neg/phantom-Eq.scala @@ -0,0 +1,39 @@ +/* This is a example of how to implement Eq using erasable phantom types. + * + * See also: ../pos/phantomEq.scala + */ + +object PhantomEqTest { + import EqUtil._ + + "abc" === "abc" + 1 === 4 + + 1 === "abc" // error + "ghi" === 4 // error + 0 === Nil // error + List(1, 2) === 1 // error + List(1, 2) === "" // error + +} + +object EqUtil extends Phantom { + + type PhantomEq[-L, -R] <: this.Any + type PhantomEqEq[T] = PhantomEq[T, T] + + implicit class EqualsDeco[T](val x: T) extends AnyVal { + def ===[U] (y: U)(implicit ce: PhantomEq[T, U]) = x.equals(y) + } + + implicit def eqString: PhantomEqEq[String] = assume[PhantomEqEq[String]] + implicit def eqInt: PhantomEqEq[Int] = assume[PhantomEqEq[Int]] + implicit def eqDouble: PhantomEqEq[Double] = assume[PhantomEqEq[Double]] + + implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] + implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = + assume[PhantomEq[Seq[T], Seq[U]]] + +} diff --git a/tests/neg/phantom-Functions.scala b/tests/neg/phantom-Functions.scala new file mode 100644 index 000000000000..35d9c633ec06 --- /dev/null +++ b/tests/neg/phantom-Functions.scala @@ -0,0 +1,6 @@ + +class PhantomFun1NoApply extends Function1[Boo.Casper, Unit] // error: class PhantomFun1NoApply needs to be abstract, since def apply: (p0: Casper)Unit is not defined + +object Boo extends Phantom { + type Casper <: this.Any +} diff --git a/tests/neg/phantom-classOf-1.scala b/tests/neg/phantom-classOf-1.scala new file mode 100644 index 000000000000..9e25d1b1a92f --- /dev/null +++ b/tests/neg/phantom-classOf-1.scala @@ -0,0 +1,10 @@ + +class phantomClassOf { + classOf[BooAny] // error + classOf[BooNothing] // error +} + +object Boo extends Phantom { + type BooAny = this.Any + type BooNothing = this.Nothing +} diff --git a/tests/neg/phantom-classOf-2.scala b/tests/neg/phantom-classOf-2.scala new file mode 100644 index 000000000000..1d3b0e3752f3 --- /dev/null +++ b/tests/neg/phantom-classOf-2.scala @@ -0,0 +1,10 @@ + +class phantomClassOf { + type Blinky <: Boo.BooAny + + classOf[Blinky] // error +} + +object Boo extends Phantom { + type BooAny = this.Any +} diff --git a/tests/neg/phantom-evidence.scala b/tests/neg/phantom-evidence.scala new file mode 100644 index 000000000000..b9e9d288867b --- /dev/null +++ b/tests/neg/phantom-evidence.scala @@ -0,0 +1,37 @@ +/* This is a example of how to implement =:= using erasable phantom types. + * + * Run this test with + * `run tests/neg/phantomEvidence-1.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + * + * See also: ../pos/phantomEvidence-1.scala + */ + + +/** In this implementation variant of =:= (called =::=) we erase all instantiations and definitions of =::= */ +object WithNormalState extends Phantom { + + type =::=[From, To] <: this.Any + + implicit inline def tpEquals[A]: A =::= A = assume[=::=[A, A]] + + trait State + sealed trait On extends State + sealed trait Off extends State + + object Instance { + def newInstance(): Instance[Off] = new Instance[Off] + } + class Instance[S <: State] private { + def getOnInstance(implicit ev: S =::= Off): Instance[On] = new Instance[On] + def getOffInstance(implicit ev: S =::= On): Instance[Off] = new Instance[Off] + } + + def run() = { + val instance = Instance.newInstance() + instance.getOffInstance // error + instance.getOnInstance.getOnInstance // error + } + +} + diff --git a/tests/neg/phantom-expr.scala b/tests/neg/phantom-expr.scala new file mode 100644 index 000000000000..c961dedecc21 --- /dev/null +++ b/tests/neg/phantom-expr.scala @@ -0,0 +1,23 @@ + +class Foo { + import Boo._ + + type Blinky <: BooAny + type Inky <: BooAny + + val blinky = Boo.boo[Blinky] + val inky = Boo.boo[Inky] + + val b = true + def fooIf1 = if (b) { blinky } else { "" } // error + def fooIf2 = if (b) { "" } else { blinky } // error + + def fooMatch1 = blinky match { case _: Blinky => () } // error + def fooMatch2 = 1 match { case 1 => 2 case _ => blinky } // error +} + +object Boo extends Phantom { + type BooAny = this.Any + def boo[B <: BooAny]: B = assume[B] +} + diff --git a/tests/neg/phantom-fun-app.scala b/tests/neg/phantom-fun-app.scala new file mode 100644 index 000000000000..bcd3a35c079a --- /dev/null +++ b/tests/neg/phantom-fun-app.scala @@ -0,0 +1,24 @@ + +class phantomFunApp { + import Boo._ // Note: this is dangerous as it imports Boo.Any as Any + + def foo1(a: Any) = ??? + def foo2(b: BooAny) = ??? + + foo1(1) + foo1(boo[Blinky]) // error + foo1(boo[Pinky]) // error + + foo2(boo[Blinky]) + foo2(boo[Pinky]) + foo2(1) // error + foo2("abc") // error + foo2(???) // error +} + +object Boo extends Phantom { + type BooAny = this.Any + type Blinky <: BooAny + type Pinky <: Blinky + def boo[B <: BooAny]: B = assume[B] +} diff --git a/tests/neg/phantom-instanceOf-1.scala b/tests/neg/phantom-instanceOf-1.scala new file mode 100644 index 000000000000..d7117d6e9562 --- /dev/null +++ b/tests/neg/phantom-instanceOf-1.scala @@ -0,0 +1,11 @@ + +class phantomInstanceOf1 { + null.asInstanceOf[Boo.Any] // error + null.asInstanceOf[Boo.Nothing] // error + "".asInstanceOf[Boo.Any] // error + "".asInstanceOf[Boo.Nothing] // error +} + +object Boo extends Phantom { + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/neg/phantom-instanceOf-2.scala b/tests/neg/phantom-instanceOf-2.scala new file mode 100644 index 000000000000..9a6fdc3ed4e5 --- /dev/null +++ b/tests/neg/phantom-instanceOf-2.scala @@ -0,0 +1,14 @@ + +class phantomInstanceOf2 { + import Boo._ + boo[Blinky].asInstanceOf[Any] // error + boo[Blinky].asInstanceOf[Nothing] // error + boo[Blinky].asInstanceOf[Blinky] // error + boo[Blinky].asInstanceOf[BooAny] // error +} + +object Boo extends Phantom { + type BooAny <: this.Any + type Blinky <: this.Any + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/neg/phantom-multiversal-AndOr.scala b/tests/neg/phantom-multiversal-AndOr.scala new file mode 100644 index 000000000000..2dee5295c99b --- /dev/null +++ b/tests/neg/phantom-multiversal-AndOr.scala @@ -0,0 +1,21 @@ + +class BooFunDef1 { + import Universe1._ + import UniverseA._ + + def fun1(b: One | A) = ??? // error + def fun2(b: A | One) = ??? // error + def fun3(b: A | One | Any) = ??? // error + + def fun4(b: A & One) = ??? // error + def fun5(b: One & A) = ??? // error + def fun6(b: A & One & Any) = ??? // error +} + +object Universe1 extends Phantom { + type One <: this.Any +} + +object UniverseA extends Phantom { + type A <: this.Any +} diff --git a/tests/neg/phantom-multiversal-type-param-bounds-1.scala b/tests/neg/phantom-multiversal-type-param-bounds-1.scala new file mode 100644 index 000000000000..db3a5c1c3b63 --- /dev/null +++ b/tests/neg/phantom-multiversal-type-param-bounds-1.scala @@ -0,0 +1,18 @@ + +class phantomTypeParamBounds1 { + import Universe1._ + import UniverseA._ + + def fun1[X >: OneNothing <: AAny] = ??? // error + def fun2[X >: ANothing <: OneAny] = ??? // error +} + +object Universe1 extends Phantom { + type OneAny = this.Any + type OneNothing = this.Nothing +} + +object UniverseA extends Phantom { + type AAny = this.Any + type ANothing = this.Nothing +} diff --git a/tests/neg/phantom-multiversal-type-param-bounds-2.scala b/tests/neg/phantom-multiversal-type-param-bounds-2.scala new file mode 100644 index 000000000000..0e085578955b --- /dev/null +++ b/tests/neg/phantom-multiversal-type-param-bounds-2.scala @@ -0,0 +1,22 @@ + +class phantomTypeParamBounds2 { + import Universe1._ + import UniverseA._ + + def fun1[X <: One & A] = ??? // error + def fun2[X <: One | A] = ??? // error + def fun3[X >: OneNothing & ANothing] = ??? // error + def fun4[X >: OneNothing | ANothing] = ??? // error + + def fun5[X >: One & A <: One & A] = ??? // error // error +} + +object Universe1 extends Phantom { + type One <: this.Any + type OneNothing = this.Nothing +} + +object UniverseA extends Phantom { + type A <: this.Any + type ANothing = this.Nothing +} diff --git a/tests/neg/phantom-multiversal.scala b/tests/neg/phantom-multiversal.scala new file mode 100644 index 000000000000..af1571033e8f --- /dev/null +++ b/tests/neg/phantom-multiversal.scala @@ -0,0 +1,35 @@ + +class BooFunDef1 { + import Universe1._ + import UniverseA._ + + fun1(one, two) + fun1(one, b) // error + fun1(b, a) // error // error + + funA(a, b) + funA(a, one) // error + funA(two, one) // error // error + + funMulti(a, one, 42) + funMulti(a, b, 42) // error + funMulti(one, two, one) // error // error + + def fun1(x: One, y: Two) = ??? + def funA(k: A, l: B) = ??? + def funMulti(k: A, x: One, i: Int) = ??? +} + +object Universe1 extends Phantom { + type One = this.Any + type Two <: One + def one: One = assume + def two: Two = assume +} + +object UniverseA extends Phantom { + type A = this.Any + type B <: A + def a: A = assume + def b: B = assume +} diff --git a/tests/neg/phantom-trait-1.scala b/tests/neg/phantom-trait-1.scala new file mode 100644 index 000000000000..2b4ca6a7b053 --- /dev/null +++ b/tests/neg/phantom-trait-1.scala @@ -0,0 +1,4 @@ + +object Boo extends Phantom { + override def assume[P >: this.Nothing <: this.Any]: P = super.assume[P] // error +} diff --git a/tests/neg/phantom-trait-2.scala b/tests/neg/phantom-trait-2.scala new file mode 100644 index 000000000000..d585db62a854 --- /dev/null +++ b/tests/neg/phantom-trait-2.scala @@ -0,0 +1,5 @@ + +object Boo1 extends Phantom { + class A extends this.Any // error + class B extends this.Nothing // error +} diff --git a/tests/neg/phantom-trait-3.scala b/tests/neg/phantom-trait-3.scala new file mode 100644 index 000000000000..1512e8e513b8 --- /dev/null +++ b/tests/neg/phantom-trait-3.scala @@ -0,0 +1,6 @@ + +class Boo1 extends Phantom // error + +trait Boo2 extends Phantom // error + +object Boo3 extends Phantom diff --git a/tests/neg/phantom-type-param-bounds-1.scala b/tests/neg/phantom-type-param-bounds-1.scala new file mode 100644 index 000000000000..5a1921c51d6b --- /dev/null +++ b/tests/neg/phantom-type-param-bounds-1.scala @@ -0,0 +1,9 @@ + +class phantomTypeParamBounds1 { + def fun5[X >: Boo.Nothing <: Any] = ??? // error + def fun6[X >: Nothing <: Boo.Any] = ??? // error +} + +object Boo extends Phantom { + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/neg/phantom-type-param-bounds-2.scala b/tests/neg/phantom-type-param-bounds-2.scala new file mode 100644 index 000000000000..9336ea821882 --- /dev/null +++ b/tests/neg/phantom-type-param-bounds-2.scala @@ -0,0 +1,13 @@ + +class phantomTypeParamBounds2 { + def fun1[X <: Boo.Any & Any] = ??? // error + def fun2[X <: Boo.Any | Any] = ??? // error + def fun3[X >: Boo.Nothing & Nothing] = ??? // error + def fun4[X >: Boo.Nothing | Nothing] = ??? // error + + def fun5[X >: Boo.Any & Any <: Boo.Any & Any] = ??? // error // error +} + +object Boo extends Phantom { + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/pos/phantom-Eq.scala b/tests/pos/phantom-Eq.scala new file mode 100644 index 000000000000..bc216b6323a0 --- /dev/null +++ b/tests/pos/phantom-Eq.scala @@ -0,0 +1,42 @@ + +/* This is a example of how to implement Eq using erasable phantom types. + * + * Run this test with + * `run tests/pos/phantomEq.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + * + * See also: ../neg/phantomEq.scala + */ + +object PhantomEq { + import EqUtil._ + + "ghi" === "jkl" + 3 === 4 + 2.0 === 3.1 + + List(1, 2) === Nil + List(1, 2) === Vector(1, 2) + + 1.toByte === (1: Number) + (1: Number) === 1.toByte +} + +object EqUtil extends Phantom { + + type PhantomEq[-L, -R] <: this.Any + type PhantomEqEq[T] = PhantomEq[T, T] + + implicit class EqualsDeco[T](val x: T) extends AnyVal { + def ===[U] (y: U)(implicit ce: PhantomEq[T, U]) = x.equals(y) + } + + implicit def eqString: PhantomEqEq[String] = assume[PhantomEqEq[String]] + implicit def eqInt: PhantomEqEq[Int] = assume[PhantomEqEq[Int]] + implicit def eqDouble: PhantomEqEq[Double] = assume[PhantomEqEq[Double]] + + implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] + implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume[PhantomEq[Seq[T], Seq[U]]] +} diff --git a/tests/pos/phantom-Eq2/Phantom-Eq_1.scala b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala new file mode 100644 index 000000000000..70a5a747409e --- /dev/null +++ b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala @@ -0,0 +1,20 @@ + +/* This is a version of ../pos/phantomEq.scala that tests phantom with separate compilation */ +object EqUtil extends Phantom { + + type PhantomEq[-L, -R] <: this.Any + type PhantomEqEq[T] = PhantomEq[T, T] + + implicit class EqualsDeco[T](val x: T) extends AnyVal { + def ===[U] (y: U)(implicit ce: PhantomEq[T, U]) = x.equals(y) + } + + implicit def eqString: PhantomEqEq[String] = assume[PhantomEqEq[String]] + implicit def eqInt: PhantomEqEq[Int] = assume[PhantomEqEq[Int]] + implicit def eqDouble: PhantomEqEq[Double] = assume[PhantomEqEq[Double]] + + implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] + implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume[PhantomEq[Seq[T], Seq[U]]] +} diff --git a/tests/pos/phantom-Eq2/Phantom-Eq_2.scala b/tests/pos/phantom-Eq2/Phantom-Eq_2.scala new file mode 100644 index 000000000000..c3f3254f6144 --- /dev/null +++ b/tests/pos/phantom-Eq2/Phantom-Eq_2.scala @@ -0,0 +1,15 @@ + +/* This is a version of ../pos/phantom.scala that tests phantom clases with separate compilation */ +object PhantomEq { + import EqUtil._ + + "ghi" === "jkl" + 3 === 4 + 2.0 === 3.1 + + List(1, 2) === Nil + List(1, 2) === Vector(1, 2) + + 1.toByte === (1: Number) + (1: Number) === 1.toByte +} diff --git a/tests/pos/phantom-Evidence.scala b/tests/pos/phantom-Evidence.scala new file mode 100644 index 000000000000..0e096177a115 --- /dev/null +++ b/tests/pos/phantom-Evidence.scala @@ -0,0 +1,38 @@ + +/* This is a example of how to implement =:= using erasable phantom types. + * + * Run this test with + * `run tests/pos/phantomEvidence-1.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + * + * See also: ../neg/phantomEvidence-1.scala + */ + +/** In this implementation variant of =:= (called =::=) we erase all instantiations and definitions of =::= */ +object WithNormalState { + import Utils._ + + trait State + sealed trait On extends State + sealed trait Off extends State + + object Instance { + def newInstance(): Instance[Off] = new Instance[Off] + } + class Instance[S <: State] private { + def getOnInstance(implicit ev: S =::= Off): Instance[On] = new Instance[On] // phantom parameter ev is erased + def getOffInstance(implicit ev: S =::= On): Instance[Off] = new Instance[Off] // phantom parameter ev is erased + } + + def run() = { + val instance = Instance.newInstance() + instance.getOnInstance // inferred phantom evidence parameter =::= is erased + instance.getOnInstance.getOffInstance.getOnInstance.getOffInstance // all inferred phantom evidence parameters =::= are erased + } + +} + +object Utils extends Phantom { + type =::=[From, To] <: this.Any + implicit def tpEquals[A]: A =::= A = assume +} diff --git a/tests/pos/phantom-Functions.scala b/tests/pos/phantom-Functions.scala new file mode 100644 index 000000000000..2e0a5dfa0bc3 --- /dev/null +++ b/tests/pos/phantom-Functions.scala @@ -0,0 +1,15 @@ + +import Phantoms._ + +class PhantomFunTArgs1 extends Function0[Casper] { + def apply() = casper +} + +class PhantomFunTArgs2 extends Function1[Casper, Casper] { + def apply(p1: Casper) = casper +} + +object Phantoms extends Phantom { + type Casper <: this.Any + def casper: Casper = assume[Casper] +} diff --git a/tests/run/phantom-1.check b/tests/run/phantom-1.check new file mode 100644 index 000000000000..3e2c972a8273 --- /dev/null +++ b/tests/run/phantom-1.check @@ -0,0 +1 @@ +fun1 diff --git a/tests/run/phantom-1.scala b/tests/run/phantom-1.scala new file mode 100644 index 000000000000..942de9dedb1f --- /dev/null +++ b/tests/run/phantom-1.scala @@ -0,0 +1,22 @@ + +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun1(Boo.any) + } + + def fun1(boo: BooAny): Unit = { + println("fun1") + } +} + +object Boo extends Phantom { + type BooAny = this.Any + def any: BooAny = assume[BooAny] +} diff --git a/tests/run/phantom-2.check b/tests/run/phantom-2.check new file mode 100644 index 000000000000..2d4b6fc3b43a --- /dev/null +++ b/tests/run/phantom-2.check @@ -0,0 +1 @@ +fun2 diff --git a/tests/run/phantom-2.scala b/tests/run/phantom-2.scala new file mode 100644 index 000000000000..d1f4e7a55150 --- /dev/null +++ b/tests/run/phantom-2.scala @@ -0,0 +1,20 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun2(Boo.nothig) + } + + def fun2(bottom: BooNothing): Unit = { + println("fun2") + } +} + +object Boo extends Phantom { + type BooNothing = this.Nothing + def nothig: BooNothing = assume[BooNothing] // Should be allowed? +} diff --git a/tests/run/phantom-3.check b/tests/run/phantom-3.check new file mode 100644 index 000000000000..86802c8797d5 --- /dev/null +++ b/tests/run/phantom-3.check @@ -0,0 +1,3 @@ +fun3 +fun3 +fun3 diff --git a/tests/run/phantom-3.scala b/tests/run/phantom-3.scala new file mode 100644 index 000000000000..3c8d1dc35152 --- /dev/null +++ b/tests/run/phantom-3.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun3(boo[Blinky], boo[Pinky]) + fun3(boo[Inky], boo[Pinky]) + fun3(boo[Pinky], boo[Casper]) + } + + def fun3(x1: Blinky, x2: Inky): Unit = { + println("fun3") + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + type Casper = Pinky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-4.check b/tests/run/phantom-4.check new file mode 100644 index 000000000000..9e4c79efcee0 --- /dev/null +++ b/tests/run/phantom-4.check @@ -0,0 +1,3 @@ +fun4 +fun4 +fun4 diff --git a/tests/run/phantom-4.scala b/tests/run/phantom-4.scala new file mode 100644 index 000000000000..86c6b4e2bac1 --- /dev/null +++ b/tests/run/phantom-4.scala @@ -0,0 +1,27 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun4(3, 4, boo[Blinky], boo[Pinky]) + fun4(5, 6, boo[Inky], boo[Pinky]) + fun4(7, 8, boo[Pinky], boo[Casper]) + } + + def fun4(n: Int, n2: Int, top: Blinky, bottom: Pinky): Unit = { + println("fun4") + } + +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + type Casper = Pinky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-5.check b/tests/run/phantom-5.check new file mode 100644 index 000000000000..019a46ac7c15 --- /dev/null +++ b/tests/run/phantom-5.check @@ -0,0 +1,3 @@ +fun5 +fun5 +fun5 diff --git a/tests/run/phantom-5.scala b/tests/run/phantom-5.scala new file mode 100644 index 000000000000..ccb0c191ded9 --- /dev/null +++ b/tests/run/phantom-5.scala @@ -0,0 +1,27 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun5(boo[Blinky])(15)(boo[Pinky])(16) + fun5(boo[Inky])(17)(boo[Pinky])(18) + fun5(boo[Pinky])(19)(boo[Casper])(20) + } + + def fun5(top: Blinky)(n: Int)(bottom: Clyde)(n2: Int): Unit = { + println("fun5") + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + type Clyde >: Pinky <: Inky + type Casper = Pinky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-FunctionXXL-1.check b/tests/run/phantom-FunctionXXL-1.check new file mode 100644 index 000000000000..d81cc0710eb6 --- /dev/null +++ b/tests/run/phantom-FunctionXXL-1.check @@ -0,0 +1 @@ +42 diff --git a/tests/run/phantom-FunctionXXL-1.scala b/tests/run/phantom-FunctionXXL-1.scala new file mode 100644 index 000000000000..55a3178d0bec --- /dev/null +++ b/tests/run/phantom-FunctionXXL-1.scala @@ -0,0 +1,19 @@ + +import Boo._ + +object Test { + + def main(args: Array[String]) = { + println(new IntPhantomFunction27().apply(pinky, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26)) + } + +} + +class IntPhantomFunction27 extends Function27[Pinky, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int] { + def apply(p1: Pinky, x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int, x11: Int, x12: Int, x13: Int, x14: Int, x15: Int, x16: Int, x17: Int, x18: Int, x19: Int, x20: Int, x21: Int, x22: Int, x23: Int, x24: Int, x25: Int, x26: Int) = 42 +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-FunctionXXL-2.check b/tests/run/phantom-FunctionXXL-2.check new file mode 100644 index 000000000000..daaac9e30302 --- /dev/null +++ b/tests/run/phantom-FunctionXXL-2.check @@ -0,0 +1,2 @@ +42 +42 diff --git a/tests/run/phantom-FunctionXXL-2.scala b/tests/run/phantom-FunctionXXL-2.scala new file mode 100644 index 000000000000..f9d5c2408ab6 --- /dev/null +++ b/tests/run/phantom-FunctionXXL-2.scala @@ -0,0 +1,78 @@ + + +object Test { + import Boo._ + + val f = (x1: Int, + x2: Int, + x3: Int, + x4: Int, + p1: Pinky, + x5: Int, + x6: Int, + x7: Int, + x8: Int, + x9: Int, + x10: Int, + x11: Int, + x12: Int, + x13: Int, + x14: Int, + x15: Int, + x16: Int, + x17: Int, + x18: Int, + x19: Int, + x20: Int, + x21: Int, + x22: Int, + x23: Int, + x24: Int, + x25: Int, + p2: Pinky, + x26: Int) => 42 + + def main(args: Array[String]) = { + val g = (x1: Int, + x2: Int, + x3: Int, + x4: Int, + x5: Int, + p1: Pinky, + x6: Int, + x7: Int, + x8: Int, + x9: Int, + x10: Int, + x11: Int, + x12: Int, + x13: Int, + x14: Int, + x15: Int, + x16: Int, + x17: Int, + x18: Int, + x19: Int, + x20: Int, + x21: Int, + x22: Int, + x23: Int, + x24: Int, + x25: Int, + x26: Int) => f(x1, x2, x3, x4, p1, x5, x6, x7, x8, x9, x10, + x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, + x21, x22, x23, x24, x25, p1, x26) + + + println(f(1, 2, 3, 4, pinky, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, pinky, 26)) + + println(g(1, 2, 3, 4, 5, pinky, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26)) + + } + +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-0.check b/tests/run/phantom-Functions-0.check new file mode 100644 index 000000000000..ca91d311132e --- /dev/null +++ b/tests/run/phantom-Functions-0.check @@ -0,0 +1,3 @@ +Blinky1.apply() +Blinky1.apply() +Blinky1.apply() diff --git a/tests/run/phantom-Functions-0.scala b/tests/run/phantom-Functions-0.scala new file mode 100644 index 000000000000..e80949311ed8 --- /dev/null +++ b/tests/run/phantom-Functions-0.scala @@ -0,0 +1,23 @@ + +import Boo._ + +object Test { + + def main(args: Array[String]): Unit = { + (new Blinky1)(pinky) + foo1(pinky) + foo2(new Blinky1) + } + + def foo1: Pinky => Unit = new Blinky1 + def foo2(fun3: Function1[Pinky, Unit]) = fun3(pinky) +} + +class Blinky1 extends Function1[Pinky, Unit] { + def apply(p1: Pinky) = println("Blinky1.apply()") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-1.check b/tests/run/phantom-Functions-1.check new file mode 100644 index 000000000000..468962bc52b3 --- /dev/null +++ b/tests/run/phantom-Functions-1.check @@ -0,0 +1,2 @@ +Blinky2.apply() +Blinky2.apply() \ No newline at end of file diff --git a/tests/run/phantom-Functions-1.scala b/tests/run/phantom-Functions-1.scala new file mode 100644 index 000000000000..8666530a716d --- /dev/null +++ b/tests/run/phantom-Functions-1.scala @@ -0,0 +1,20 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky2)(pinky, pinky) + bar1(pinky, pinky) + } + val bar1: (Pinky, Pinky) => Unit = new Blinky2 + +} + +class Blinky2 extends Function2[Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky) = println("Blinky2.apply()") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-2.check b/tests/run/phantom-Functions-2.check new file mode 100644 index 000000000000..afbb7b2c1d50 --- /dev/null +++ b/tests/run/phantom-Functions-2.check @@ -0,0 +1,4 @@ +Blinky.apply(42) +Blinky.apply(43) +Blinky.apply(44) +Blinky.apply(47) diff --git a/tests/run/phantom-Functions-2.scala b/tests/run/phantom-Functions-2.scala new file mode 100644 index 000000000000..e846527c075f --- /dev/null +++ b/tests/run/phantom-Functions-2.scala @@ -0,0 +1,24 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky)(42, pinky) + (new Blinky).apply(43, pinky) + + foo1(44, pinky) + foo2(new Blinky) + } + + def foo1: (Int, Pinky) => Unit = new Blinky + def foo2(boo: Function2[Int, Pinky, Unit]) = boo(47, pinky) +} + +class Blinky extends Function2[Int, Pinky, Unit] { + def apply(p1: Int, p2: Pinky) = println("Blinky.apply(" + p1 + ")") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-3.check b/tests/run/phantom-Functions-3.check new file mode 100644 index 000000000000..69d47b850a0f --- /dev/null +++ b/tests/run/phantom-Functions-3.check @@ -0,0 +1 @@ +Blinky.apply(44) diff --git a/tests/run/phantom-Functions-3.scala b/tests/run/phantom-Functions-3.scala new file mode 100644 index 000000000000..b1fd5535b2f9 --- /dev/null +++ b/tests/run/phantom-Functions-3.scala @@ -0,0 +1,19 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(44, pinky, 0.4, pinky, pinky) + } + + def foo3: (Int, Pinky, Double, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Function5[Int, Pinky, Double, Pinky, Pinky, Unit] { + def apply(p1: Int, p2: Pinky, p3: Double, p4: Pinky, p5: Pinky) = println("Blinky.apply(" + p1 + ")") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-4.check b/tests/run/phantom-Functions-4.check new file mode 100644 index 000000000000..69d47b850a0f --- /dev/null +++ b/tests/run/phantom-Functions-4.check @@ -0,0 +1 @@ +Blinky.apply(44) diff --git a/tests/run/phantom-Functions-4.scala b/tests/run/phantom-Functions-4.scala new file mode 100644 index 000000000000..defecdc6c90f --- /dev/null +++ b/tests/run/phantom-Functions-4.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(44, pinky, 0.4, pinky, pinky) + } + + val foo3: (Int, Pinky, Double, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky + +class Blinky extends Function5[Int, Pinky, Double, Pinky, Pinky, Unit] { + def apply(p1: Int, p2: Pinky, p3: Double, p4: Pinky, p5: Pinky) = println("Blinky.apply(" + p1 + ")") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-5.check b/tests/run/phantom-Functions-5.check new file mode 100644 index 000000000000..455a2252172d --- /dev/null +++ b/tests/run/phantom-Functions-5.check @@ -0,0 +1 @@ +Blinky2.apply(44) diff --git a/tests/run/phantom-Functions-5.scala b/tests/run/phantom-Functions-5.scala new file mode 100644 index 000000000000..f543c75a9721 --- /dev/null +++ b/tests/run/phantom-Functions-5.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(44, pinky, 0.4, pinky, pinky) + } + + val foo3: (Int, Pinky, Double, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky { + def apply(p1: Int, p2: Pinky, p3: Double, p4: Pinky, p5: Pinky) = println("Blinky2.apply(" + p1 + ")") +} + +abstract class Blinky extends Function5[Int, Pinky, Double, Pinky, Pinky, Unit] + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-6.check b/tests/run/phantom-Functions-6.check new file mode 100644 index 000000000000..455a2252172d --- /dev/null +++ b/tests/run/phantom-Functions-6.check @@ -0,0 +1 @@ +Blinky2.apply(44) diff --git a/tests/run/phantom-Functions-6.scala b/tests/run/phantom-Functions-6.scala new file mode 100644 index 000000000000..d0262f5976c3 --- /dev/null +++ b/tests/run/phantom-Functions-6.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(44, pinky, 0.4, pinky, pinky) + } + + val foo3: (Int, Pinky, Double, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky { + def apply(p1: Int, p2: Pinky, p3: Double, p4: Pinky, p5: Pinky) = println("Blinky2.apply(" + p1 + ")") +} + +trait Blinky extends Function5[Int, Pinky, Double, Pinky, Pinky, Unit] + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-7.check b/tests/run/phantom-Functions-7.check new file mode 100644 index 000000000000..69d47b850a0f --- /dev/null +++ b/tests/run/phantom-Functions-7.check @@ -0,0 +1 @@ +Blinky.apply(44) diff --git a/tests/run/phantom-Functions-7.scala b/tests/run/phantom-Functions-7.scala new file mode 100644 index 000000000000..f5b71c9f6a40 --- /dev/null +++ b/tests/run/phantom-Functions-7.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(44, pinky, 0.4, pinky, pinky) + } + + val foo3: (Int, Pinky, Double, Pinky, Pinky) => Unit = new Blinky2().asInstanceOf[Function5[Int, Pinky, Double, Pinky, Pinky, Unit]] +} + +class Blinky2 extends Blinky + +trait Blinky extends Function5[Int, Pinky, Double, Pinky, Pinky, Unit] { + def apply(p1: Int, p2: Pinky, p3: Double, p4: Pinky, p5: Pinky) = println("Blinky.apply(" + p1 + ")") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-8.check b/tests/run/phantom-Functions-8.check new file mode 100644 index 000000000000..ca91d311132e --- /dev/null +++ b/tests/run/phantom-Functions-8.check @@ -0,0 +1,3 @@ +Blinky1.apply() +Blinky1.apply() +Blinky1.apply() diff --git a/tests/run/phantom-Functions-8.scala b/tests/run/phantom-Functions-8.scala new file mode 100644 index 000000000000..dfd8ae7bc574 --- /dev/null +++ b/tests/run/phantom-Functions-8.scala @@ -0,0 +1,25 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky1)(pinky) + foo1(pinky) + foo2(new Blinky1) + } + + def foo1: Pinky => Pinky = new Blinky1 + def foo2(boo: Function1[Pinky, Pinky]) = boo(pinky) +} + +class Blinky1 extends Function1[Pinky, Pinky] { + def apply(p1: Pinky) = { + println("Blinky1.apply()") + p1 + } +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-9.check b/tests/run/phantom-Functions-9.check new file mode 100644 index 000000000000..ca91d311132e --- /dev/null +++ b/tests/run/phantom-Functions-9.check @@ -0,0 +1,3 @@ +Blinky1.apply() +Blinky1.apply() +Blinky1.apply() diff --git a/tests/run/phantom-Functions-9.scala b/tests/run/phantom-Functions-9.scala new file mode 100644 index 000000000000..045695192b46 --- /dev/null +++ b/tests/run/phantom-Functions-9.scala @@ -0,0 +1,25 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky1)(42) + foo1(42) + foo2(new Blinky1) + } + + def foo1: Int => Pinky = new Blinky1 + def foo2(boo: Function1[Int, Pinky]) = boo(42) +} + +class Blinky1 extends Function1[Int, Pinky] { + def apply(i: Int) = { + println("Blinky1.apply()") + pinky + } +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-ImplicitFunctions-0.check b/tests/run/phantom-ImplicitFunctions-0.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/run/phantom-ImplicitFunctions-0.scala b/tests/run/phantom-ImplicitFunctions-0.scala new file mode 100644 index 000000000000..6a68f21475ce --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-0.scala @@ -0,0 +1,67 @@ + +object Boo extends Phantom { + type Slimer <: this.Any + implicit def phantom: Slimer = assume[Slimer] +} + + +object Test { + + def main(args: Array[String]): Unit = { + implicit val world: String = "world!" + import Boo._ + + val i1 = (implicit (s: Slimer) => true) + val i2 = {implicit (s: Slimer) => false} + + assert(i1) + assert(!i2) + + val x: implicit (String, Slimer) => Boolean = { implicit (s: String, p: Slimer) => s.length > 2 } + + val xx: implicit (String, Int, Slimer) => Int = implicit (x: String, y: Int, p: Slimer) => x.length + y + + val y: (String, Slimer) => Boolean = x + + object nested { + implicit val empty: String = "" + assert(!x) + } + + val yy: (String, Int, Slimer) => Any = xx + + val z1: implicit (String, Slimer) => Boolean = implicitly[String].length >= 2 + assert(z1) + + type StringlyBool = implicit (String, Slimer) => Boolean + + val z2: StringlyBool = implicitly[String].length >= 2 + assert(z2) + + type Stringly[T] = implicit (String, Slimer) => T + + val z3: Stringly[Boolean] = implicitly[String].length >= 2 + assert(z3) + + type GenericImplicit[X, Y <: Slimer] = implicit (X, Y) => Boolean + + val z4: GenericImplicit[String, Slimer] = implicitly[String].length >= 2 + assert(z4) + + val b = x("hello", phantom) + + val b1: Boolean = b + + val bi = x + + val bi1: Boolean = bi + + val c = xx("hh", 22, phantom) + + val c1: Int = c + + def foo(s: String): Stringly[Int] = 42 + + (if ("".isEmpty) foo("") else foo("")).apply("", phantom) + } +} diff --git a/tests/run/phantom-ImplicitFunctions-1.check b/tests/run/phantom-ImplicitFunctions-1.check new file mode 100644 index 000000000000..8baef1b4abc4 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-1.check @@ -0,0 +1 @@ +abc diff --git a/tests/run/phantom-ImplicitFunctions-1.scala b/tests/run/phantom-ImplicitFunctions-1.scala new file mode 100644 index 000000000000..f47013b93aef --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-1.scala @@ -0,0 +1,18 @@ + +object Test { + import CanPrintPhantoms._ + + type Ctx[T] = implicit CanPrint => T + + def contextualPrintln(s: String): Ctx[Unit] = implicit (canPrint: CanPrint) => println(s) + + def main(args: Array[String]) = { + implicit def ev: CanPrint = canPrint + contextualPrintln("abc") + } +} + +object CanPrintPhantoms extends Phantom { + type CanPrint <: this.Any + def canPrint: CanPrint = assume +} diff --git a/tests/run/phantom-ImplicitFunctions-2.check b/tests/run/phantom-ImplicitFunctions-2.check new file mode 100644 index 000000000000..893d9bc9e42c --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-2.check @@ -0,0 +1,5 @@ +result: 4 +******* log ******** +first step: 1 +second step: 2 +third step: 4 diff --git a/tests/run/phantom-ImplicitFunctions-2.scala b/tests/run/phantom-ImplicitFunctions-2.scala new file mode 100644 index 000000000000..10847f2b1ef5 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-2.scala @@ -0,0 +1,52 @@ + +import collection.mutable.ListBuffer + +class Transaction { + private val log = new ListBuffer[String] + def println(s: String): Unit = log += s + + private var aborted = false + private var committed = false + + def abort(): Unit = { aborted = true } + def isAborted = aborted + + def commit(): Unit = + if (!aborted && !committed) { + Console.println("******* log ********") + log.foreach(Console.println) + committed = true + } +} + +object Test extends Phantom { + type CanDoTransaction <: this.Any + + def transaction[T](op: (Transaction, CanDoTransaction) => T) = { + val trans: Transaction = new Transaction + op(trans, assume[CanDoTransaction]) + trans.commit() + } + + def f1(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"first step: $x") + f2(x + 1) + } + def f2(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"second step: $x") + f3(x * x) + } + def f3(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"third step: $x") + if (x % 2 != 0) thisTransaction.abort() + x + } + + def main(args: Array[String]) = { + transaction { + implicit (thisTransaction, canDo) => + val res = f1(args.length) + println(if (thisTransaction.isAborted) "aborted" else s"result: $res") + } + } +} diff --git a/tests/run/phantom-ImplicitFunctions-3.check b/tests/run/phantom-ImplicitFunctions-3.check new file mode 100644 index 000000000000..893d9bc9e42c --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-3.check @@ -0,0 +1,5 @@ +result: 4 +******* log ******** +first step: 1 +second step: 2 +third step: 4 diff --git a/tests/run/phantom-ImplicitFunctions-3.scala b/tests/run/phantom-ImplicitFunctions-3.scala new file mode 100644 index 000000000000..b6bd2ae91d21 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-3.scala @@ -0,0 +1,53 @@ + +import collection.mutable.ListBuffer + +class Transaction { + private val log = new ListBuffer[String] + def println(s: String): Unit = log += s + + private var aborted = false + private var committed = false + + def abort(): Unit = { aborted = true } + def isAborted = aborted + + def commit(): Unit = + if (!aborted && !committed) { + Console.println("******* log ********") + log.foreach(Console.println) + committed = true + } +} + +object Test extends Phantom { + type CanDoTransaction <: this.Any + + def transaction[T](op: (Transaction, CanDoTransaction) => T) = { + val trans: Transaction = new Transaction + op.apply(trans, assume[CanDoTransaction]) + trans.commit() + } + + def thisTransaction = $t: Transaction => $t + + def f1(x: Int) = { ($t: Transaction, $c: CanDoTransaction) => + thisTransaction.apply($t).println(s"first step: $x") + f2(x + 1).apply($t, $c) + } + def f2(x: Int) = { ($t: Transaction, $c: CanDoTransaction) => + thisTransaction.apply($t).println(s"second step: $x") + f3(x * x).apply($t, $c) + } + def f3(x: Int) = { ($t: Transaction, $c: CanDoTransaction) => + thisTransaction.apply($t).println(s"third step: $x") + if (x % 2 != 0) thisTransaction.apply($t).abort() + x + } + + def main(args: Array[String]) = { + transaction { ($t, $c) => + val res = f1(args.length).apply($t, $c) + println(if (thisTransaction.apply($t).isAborted) "aborted" else s"result: $res") + } + } +} diff --git a/tests/run/phantom-Lambda-0.check b/tests/run/phantom-Lambda-0.check new file mode 100644 index 000000000000..f70d7bba4ae1 --- /dev/null +++ b/tests/run/phantom-Lambda-0.check @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/tests/run/phantom-Lambda-0.scala b/tests/run/phantom-Lambda-0.scala new file mode 100644 index 000000000000..bbdbcc78a8e2 --- /dev/null +++ b/tests/run/phantom-Lambda-0.scala @@ -0,0 +1,13 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + (((b, i) => println(i)): ((Pinky, Int) => Unit)).apply(pinky, 42) + } +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Lambda-1.check b/tests/run/phantom-Lambda-1.check new file mode 100644 index 000000000000..463021151d29 --- /dev/null +++ b/tests/run/phantom-Lambda-1.check @@ -0,0 +1,2 @@ +foo1 +foo2 diff --git a/tests/run/phantom-Lambda-1.scala b/tests/run/phantom-Lambda-1.scala new file mode 100644 index 000000000000..f17ad89f896d --- /dev/null +++ b/tests/run/phantom-Lambda-1.scala @@ -0,0 +1,17 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + foo1(casper) + foo2(casper) + } + + def foo1: Casper => Unit = b => println("foo1") + def foo2 = (b: Casper) => println("foo2") +} + +object Boo extends Phantom { + type Casper <: Boo.Any + def casper: Casper = assume +} diff --git a/tests/run/phantom-Lambda-2.check b/tests/run/phantom-Lambda-2.check new file mode 100644 index 000000000000..45679f5c2d60 --- /dev/null +++ b/tests/run/phantom-Lambda-2.check @@ -0,0 +1,2 @@ +bar1 +bar2 diff --git a/tests/run/phantom-Lambda-2.scala b/tests/run/phantom-Lambda-2.scala new file mode 100644 index 000000000000..69d403b0fbfb --- /dev/null +++ b/tests/run/phantom-Lambda-2.scala @@ -0,0 +1,20 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + val foo = (b: Casper, i: Int) => i + foo(casper, 43) + + bar1(casper, casper) + bar2(casper, casper) + } + + val bar1: (Casper, Casper) => Unit = (b, b1) => println("bar1") + val bar2 = (b: Casper, b2: Casper) => println("bar2") +} + +object Boo extends Phantom { + type Casper <: Boo.Any + def casper: Casper = assume +} diff --git a/tests/run/phantom-OnHList.check b/tests/run/phantom-OnHList.check new file mode 100644 index 000000000000..77a9e85ab6f2 --- /dev/null +++ b/tests/run/phantom-OnHList.check @@ -0,0 +1,4 @@ +() +(s,) +(1.0, 2.0, 3.0) +(s, 1.0, 2.0, 3.0) \ No newline at end of file diff --git a/tests/run/phantom-OnHList.scala b/tests/run/phantom-OnHList.scala new file mode 100644 index 000000000000..ee5da40619da --- /dev/null +++ b/tests/run/phantom-OnHList.scala @@ -0,0 +1,100 @@ + +/* Run this test with + * `run tests/run/phantomOnHList.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + * + * Disclaimer: This is only a prototype of HList that uses phantom evidences for the append operation + */ +object Test { + + + def main(args: Array[String]): Unit = { + println(HNil) + + val l1: String :: HNil = HList1("s") + println(l1) + + val l3: Double :: Double :: Double :: HNil = HList3(1d, 2d, 3d) + println(l3) + + val l4: String :: Double :: Double :: Double :: HNil = HListN[String, Double :: Double :: Double :: HNil](Array("s", 1d, 2d, 3d)) + println(l4) + } + +} + +// HList types ------------------------------------------------------------------------------------ + +sealed trait HList { def underlying: Array[Any] } +sealed trait ::[H, T <: HList] extends HList // Should be [+H, +T <: HList], see #1500 +sealed trait HNil extends HList + +// HList values ----------------------------------------------------------------------------------- + +final case object HNil extends HNil { + val underlying: Array[Any] = Array.empty[Any] + override def toString(): String = "()" +} + +// Case class based HLists for small sizes -------------------------------------------------------- + +final case class HList1[T1](e1: T1) extends (T1 :: HNil) { + def underlying: Array[Any] = Array(e1) + override def toString(): String = s"($e1,)" +} + +final case class HList2[T1, T2](e1: T1, e2: T2) extends (T1 :: T2 :: HNil) { + def underlying: Array[Any] = Array(e1, e2) + override def toString(): String = s"($e1, $e2)" +} + +final case class HList3[T1, T2, T3](e1: T1, e2: T2, e3: T3) extends (T1 :: T2 :: T3 :: HNil) { + def underlying: Array[Any] = Array(e1, e2, e3) + override def toString(): String = s"($e1, $e2, $e3)" +} + +// Array based HLists for large sizes ------------------------------------------------------------- + +final case class HListN[H, T <: HList](underlying: Array[Any]) extends (H :: T) { + override def toString() = underlying.mkString("(", ", ", ")") + + override def equals(o: Any): Boolean = + o match { + case l: HListN[_, _] => l.underlying.sameElements(underlying) + case _ => false + } + + override def hashCode: Int = { + var r = 1 + for (e <- underlying) + r = 31 * r + e.## + r + } +} + +object HListUnapply { + def unapplySeq[L <: HList](l: L): Option[Seq[Any]] = Some(l.underlying) +} + +// Low level (Array based) HLists Appender -------------------------------------------------------- + +trait Appender[L1 <: HList, L2 <: HList] { + type Out <: HList + def apply(l1: L1, l2: L2): Out +} + +object Appender { + implicit def lowLevelAppender[L1 <: HList, L2 <: HList, O <: HList](implicit p: PhantomAppender.Aux[L1, L2, O]): Appender[L1, L2] { type Out = O } = + new Appender[L1, L2] { + type Out = O + def apply(l1: L1, l2: L2): Out = HListN(Array.concat(l1.underlying, l2.underlying)).asInstanceOf[O] + } +} + +// Type level "only" computation of type Out ------------------------------------------------------ + +object PhantomAppender extends Phantom { + type Aux[L1 <: HList, L2 <: HList, O <: HList] <: this.Any + implicit def caseHNil[L <: HList]: Aux[HNil, L, L] = assume + implicit def caseHCons[H, T <: HList, L <: HList, O <: HList](implicit p: Aux[T, L, O]): Aux[H :: T, L, H :: O] = assume +} diff --git a/tests/run/phantom-anonimous-functions.check b/tests/run/phantom-anonimous-functions.check new file mode 100644 index 000000000000..ecdf81daa328 --- /dev/null +++ b/tests/run/phantom-anonimous-functions.check @@ -0,0 +1,3 @@ +AnnonPhantomsFunction1.apply() 1 +AnnonPhantomsFunction1.apply() 2 +AnnonPhantomsFunction1.apply() 3 diff --git a/tests/run/phantom-anonimous-functions.scala b/tests/run/phantom-anonimous-functions.scala new file mode 100644 index 000000000000..a1bbce15d79c --- /dev/null +++ b/tests/run/phantom-anonimous-functions.scala @@ -0,0 +1,25 @@ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + val anonPhantomsFunction1 = new Function1[Blinky, Unit] { + def apply(p1: Blinky) = println("AnnonPhantomsFunction1.apply() 1") + } + anonPhantomsFunction1(boo[Blinky]) + + (new Function1[Blinky, Unit] { + def apply(p1: Blinky) = println("AnnonPhantomsFunction1.apply() 2") + }).apply(boo[Blinky]) + + (new Function2[Blinky, Int, Unit] { + def apply(p1: Blinky, i: Int) = println("AnnonPhantomsFunction1.apply() " + i) + }).apply(boo[Blinky], 3) + + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-decls-1.check b/tests/run/phantom-decls-1.check new file mode 100644 index 000000000000..b50ae590ec5c --- /dev/null +++ b/tests/run/phantom-decls-1.check @@ -0,0 +1,4 @@ +Boo1 +Boo1.polyfun1 +Boo1 +Boo1.polyfun1 diff --git a/tests/run/phantom-decls-1.scala b/tests/run/phantom-decls-1.scala new file mode 100644 index 000000000000..f73c88f3c4bb --- /dev/null +++ b/tests/run/phantom-decls-1.scala @@ -0,0 +1,29 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + new Boo1[BooAny]().polyfun1(boo[Blinky]) + new Boo1[BooAny]().polyfun1(boo[Inky]) + } + + def fun(top: BooAny): Unit = () + + class Boo1[P <: BooAny] { + println("Boo1") + def polyfun1(p1: P): Unit = { + println("Boo1.polyfun1") + } + } +} + +object Boo extends Phantom { + type BooAny = this.Any + type Blinky <: this.Any + type Inky <: Blinky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-decls-2.check b/tests/run/phantom-decls-2.check new file mode 100644 index 000000000000..12166f8878fb --- /dev/null +++ b/tests/run/phantom-decls-2.check @@ -0,0 +1,6 @@ +Boo2 +Boo2.polyfun1 +Boo2 +Boo2.polyfun1 +Boo2 +Boo2.polyfun1 diff --git a/tests/run/phantom-decls-2.scala b/tests/run/phantom-decls-2.scala new file mode 100644 index 000000000000..176c28bb6d1a --- /dev/null +++ b/tests/run/phantom-decls-2.scala @@ -0,0 +1,31 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + new Boo2().polyfun1(boo[Blinky]) + new Boo2().polyfun1(boo[Inky]) + new Boo2().polyfun1(boo[Pinky]) + } + + class Boo2 { + println("Boo2") + type Boo3 = BooAny + def polyfun1(p2: Boo3): Unit = { + println("Boo2.polyfun1") + } + } +} + +object Boo extends Phantom { + type BooAny = this.Any + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + type Casper = Pinky + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/run/phantom-decls-3.check b/tests/run/phantom-decls-3.check new file mode 100644 index 000000000000..0e209e00a5bd --- /dev/null +++ b/tests/run/phantom-decls-3.check @@ -0,0 +1,4 @@ +Boo3 +Boo3.polyfun1 +Boo3 +Boo3.polyfun1 diff --git a/tests/run/phantom-decls-3.scala b/tests/run/phantom-decls-3.scala new file mode 100644 index 000000000000..e7137ae8a412 --- /dev/null +++ b/tests/run/phantom-decls-3.scala @@ -0,0 +1,36 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + + new Boo3(){ + type Boo1 = BooAny + }.polyfun1(boo[Pinky]) + + new Boo3(){ + type Boo1 = Blinky + }.polyfun1(boo[Blinky]) + + } + + trait Boo3 { + println("Boo3") + type Boo1 <: BooAny + def polyfun1(p3: Boo1): Unit = { + println("Boo3.polyfun1") + } + } +} + +object Boo extends Phantom { + type BooAny = this.Any + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: BooAny]: B = assume[B] +} diff --git a/tests/run/phantom-decls-4.check b/tests/run/phantom-decls-4.check new file mode 100644 index 000000000000..ab02977bcee4 --- /dev/null +++ b/tests/run/phantom-decls-4.check @@ -0,0 +1,3 @@ +Boo4 +Boo4 +Boo4 diff --git a/tests/run/phantom-decls-4.scala b/tests/run/phantom-decls-4.scala new file mode 100644 index 000000000000..abc0e8fb5ff3 --- /dev/null +++ b/tests/run/phantom-decls-4.scala @@ -0,0 +1,25 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + new Boo4(boo[Blinky]) + new Boo4(boo[Inky]) + new Boo4(boo[Pinky]) + } + + class Boo4(p4: Blinky) { + println("Boo4") + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-decls-5.check b/tests/run/phantom-decls-5.check new file mode 100644 index 000000000000..2a678b77b83a --- /dev/null +++ b/tests/run/phantom-decls-5.check @@ -0,0 +1,2 @@ +Boo5 +Boo5 diff --git a/tests/run/phantom-decls-5.scala b/tests/run/phantom-decls-5.scala new file mode 100644 index 000000000000..edb404ef1ab0 --- /dev/null +++ b/tests/run/phantom-decls-5.scala @@ -0,0 +1,24 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + new Boo5[Blinky](boo[Pinky]) + new Boo5[Pinky](boo[Pinky]) + } + + class Boo5[P <: Blinky](p5: P) { + println("Boo5") + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-hk-1.check b/tests/run/phantom-hk-1.check new file mode 100644 index 000000000000..79a820550a35 --- /dev/null +++ b/tests/run/phantom-hk-1.check @@ -0,0 +1,3 @@ +hkFun1 +hkFun1 +hkFun1 diff --git a/tests/run/phantom-hk-1.scala b/tests/run/phantom-hk-1.scala new file mode 100644 index 000000000000..0f4a8b48baa3 --- /dev/null +++ b/tests/run/phantom-hk-1.scala @@ -0,0 +1,31 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + hkFun1(boo[Blinky]) + hkFun1(boo[Inky]) + hkFun1(boo[Pinky]) + } + + type HKPhantom[X <: Blinky] = X + + def hkFun1[Y <: Blinky](p9: HKPhantom[Y]) = { + println("hkFun1") + } + +} + +trait Phantoms { +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/run/phantom-hk-2.check b/tests/run/phantom-hk-2.check new file mode 100644 index 000000000000..a36ae238b71d --- /dev/null +++ b/tests/run/phantom-hk-2.check @@ -0,0 +1,3 @@ +hk2 +hk2 +hk2 diff --git a/tests/run/phantom-hk-2.scala b/tests/run/phantom-hk-2.scala new file mode 100644 index 000000000000..d75cf13c5289 --- /dev/null +++ b/tests/run/phantom-hk-2.scala @@ -0,0 +1,29 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + type HKPhantom[X <: BooAny] = X + + def main(args: Array[String]): Unit = { + fun(hkFun2(boo[Blinky])) + fun(hkFun2(boo[Inky])) + fun(hkFun2(boo[Pinky])) + } + + def fun(top: BooAny): Unit = println("hk2") + + def hkFun2[Y <: BooAny](p10: HKPhantom[Y]): HKPhantom[Y] = p10 +} + + +object Boo extends Phantom { + type BooAny = Boo.Any + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-lazy-val.check b/tests/run/phantom-lazy-val.check new file mode 100644 index 000000000000..069e33cfba04 --- /dev/null +++ b/tests/run/phantom-lazy-val.check @@ -0,0 +1,3 @@ +1 +foo +2 diff --git a/tests/run/phantom-lazy-val.scala b/tests/run/phantom-lazy-val.scala new file mode 100644 index 000000000000..b6214b9b40cf --- /dev/null +++ b/tests/run/phantom-lazy-val.scala @@ -0,0 +1,27 @@ + +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + println(1) + foo + println(2) + foo + } + + lazy val foo = { + println("foo") + any + } + +} + +object Boo extends Phantom { + type BooAny = this.Any + def any: BooAny = assume[BooAny] +} diff --git a/tests/run/phantom-methods-1.check b/tests/run/phantom-methods-1.check new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/run/phantom-methods-1.scala b/tests/run/phantom-methods-1.scala new file mode 100644 index 000000000000..c55dce758219 --- /dev/null +++ b/tests/run/phantom-methods-1.scala @@ -0,0 +1,22 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun(phantomFun1()) + } + + def fun(top: BooAny): Unit = () + + def phantomFun1(): Pinky = boo[Pinky] +} + +object Boo extends Phantom { + type BooAny = Boo.Any + type Pinky <: Boo.Any + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-10.check b/tests/run/phantom-methods-10.check new file mode 100644 index 000000000000..50cb66f41e35 --- /dev/null +++ b/tests/run/phantom-methods-10.check @@ -0,0 +1,3 @@ +fun +inky +pacFun4 diff --git a/tests/run/phantom-methods-10.scala b/tests/run/phantom-methods-10.scala new file mode 100644 index 000000000000..a9bc3a11b01c --- /dev/null +++ b/tests/run/phantom-methods-10.scala @@ -0,0 +1,31 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomRefErasure,phantomErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun2.pacFun4(inky) + } + + def pacFun4(clyde: Inky) = { + println("pacFun4") + } + + def inky: Inky = { + println("inky") + boo[Inky] + } + + def fun2 = { + println("fun") + this + } +} + +object Boo extends Phantom { + type Inky <: this.Any + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-2.check b/tests/run/phantom-methods-2.check new file mode 100644 index 000000000000..1e48428f80bc --- /dev/null +++ b/tests/run/phantom-methods-2.check @@ -0,0 +1,3 @@ +fun +fun +fun diff --git a/tests/run/phantom-methods-2.scala b/tests/run/phantom-methods-2.scala new file mode 100644 index 000000000000..17e7f1982a06 --- /dev/null +++ b/tests/run/phantom-methods-2.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun(phantomFun2(Boo.boo[Blinky])) + fun(phantomFun2(Boo.boo[Inky])) + fun(phantomFun2(Boo.boo[Pinky])) + } + + def fun(top: Blinky): Unit = println("fun") + + def phantomFun2(p6: Blinky): Blinky = p6 + +} + +object Boo extends Phantom { + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-3.check b/tests/run/phantom-methods-3.check new file mode 100644 index 000000000000..1e48428f80bc --- /dev/null +++ b/tests/run/phantom-methods-3.check @@ -0,0 +1,3 @@ +fun +fun +fun diff --git a/tests/run/phantom-methods-3.scala b/tests/run/phantom-methods-3.scala new file mode 100644 index 000000000000..e74b03eb6022 --- /dev/null +++ b/tests/run/phantom-methods-3.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun(phantomFun3(boo[Blinky])) + fun(phantomFun3(boo[Inky])) + fun(phantomFun3(boo[Pinky])) + } + + def fun(top: Blinky): Unit = println("fun") + + def phantomFun3[P <: Blinky](p7: P): Blinky = p7 + +} + +object Boo extends Phantom { + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-4.check b/tests/run/phantom-methods-4.check new file mode 100644 index 000000000000..1e48428f80bc --- /dev/null +++ b/tests/run/phantom-methods-4.check @@ -0,0 +1,3 @@ +fun +fun +fun diff --git a/tests/run/phantom-methods-4.scala b/tests/run/phantom-methods-4.scala new file mode 100644 index 000000000000..0843c106d512 --- /dev/null +++ b/tests/run/phantom-methods-4.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun(phantomFun4(boo[Blinky])) + fun(phantomFun4(boo[Inky])) + fun(phantomFun4(boo[Blinky])) + } + + def fun(top: Blinky): Unit = println("fun") + + def phantomFun4[G <: Blinky](p8: G): G = p8 + +} + +object Boo extends Phantom { + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-5.check b/tests/run/phantom-methods-5.check new file mode 100644 index 000000000000..4e29067a1ff3 --- /dev/null +++ b/tests/run/phantom-methods-5.check @@ -0,0 +1,3 @@ +customFun1 +customFun1 +customFun1 diff --git a/tests/run/phantom-methods-5.scala b/tests/run/phantom-methods-5.scala new file mode 100644 index 000000000000..354f1aa4f50a --- /dev/null +++ b/tests/run/phantom-methods-5.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + pacFun1(boo[Blinky]) + pacFun1(boo[Inky]) + pacFun1(boo[Pinky]) + } + + def pacFun1(blinky: Blinky) = { + println("customFun1") + } + +} + +object Boo extends Phantom { + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-6.check b/tests/run/phantom-methods-6.check new file mode 100644 index 000000000000..477c5c7817c8 --- /dev/null +++ b/tests/run/phantom-methods-6.check @@ -0,0 +1 @@ +customPhantomsFun2 diff --git a/tests/run/phantom-methods-6.scala b/tests/run/phantom-methods-6.scala new file mode 100644 index 000000000000..c609d23cc7d2 --- /dev/null +++ b/tests/run/phantom-methods-6.scala @@ -0,0 +1,24 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + pacFun2(boo[Pinky]) + } + + def pacFun2(pinky: Pinky) = { + println("customPhantomsFun2") + } + +} + +object Boo extends Phantom { + type Blinky <: Boo.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-7.check b/tests/run/phantom-methods-7.check new file mode 100644 index 000000000000..002faad12556 --- /dev/null +++ b/tests/run/phantom-methods-7.check @@ -0,0 +1,2 @@ +pacFun3 +pacFun3 diff --git a/tests/run/phantom-methods-7.scala b/tests/run/phantom-methods-7.scala new file mode 100644 index 000000000000..b7128aad2ce1 --- /dev/null +++ b/tests/run/phantom-methods-7.scala @@ -0,0 +1,24 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + pacFun3(boo[Clyde]) + pacFun3(boo[Pinky]) + } + + def pacFun3(clyde: Clyde) = { + println("pacFun3") + } +} + +object Boo extends Phantom { + type Inky <: Boo.Any + type Pinky <: Inky + type Clyde >: Pinky <: Inky + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-8.check b/tests/run/phantom-methods-8.check new file mode 100644 index 000000000000..1046cb49e89d --- /dev/null +++ b/tests/run/phantom-methods-8.check @@ -0,0 +1,2 @@ +inky +pacFun4 diff --git a/tests/run/phantom-methods-8.scala b/tests/run/phantom-methods-8.scala new file mode 100644 index 000000000000..6c3d89942a44 --- /dev/null +++ b/tests/run/phantom-methods-8.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomRefErasure,phantomErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + pacFun4(inky) + } + + def pacFun4(clyde: Inky) = { + println("pacFun4") + } + + def inky: Inky = { + println("inky") + Boo.boo[Inky] + } +} + +object Boo extends Phantom { + type Inky <: Boo.Any + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-methods-9.check b/tests/run/phantom-methods-9.check new file mode 100644 index 000000000000..50cb66f41e35 --- /dev/null +++ b/tests/run/phantom-methods-9.check @@ -0,0 +1,3 @@ +fun +inky +pacFun4 diff --git a/tests/run/phantom-methods-9.scala b/tests/run/phantom-methods-9.scala new file mode 100644 index 000000000000..1a6cce79d3ad --- /dev/null +++ b/tests/run/phantom-methods-9.scala @@ -0,0 +1,31 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomRefErasure,phantomErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + fun1().pacFun4(inky) + } + + def pacFun4(clyde: Inky) = { + println("pacFun4") + } + + def inky: Inky = { + println("inky") + boo[Inky] + } + + def fun1() = { + println("fun") + this + } +} + +object Boo extends Phantom { + type Inky <: Boo.Any + def boo[B <: Boo.Any]: B = assume[B] +} diff --git a/tests/run/phantom-poly-1.check b/tests/run/phantom-poly-1.check new file mode 100644 index 000000000000..e2ee46abe5fe --- /dev/null +++ b/tests/run/phantom-poly-1.check @@ -0,0 +1,2 @@ +polyfun1 +polyfun1 diff --git a/tests/run/phantom-poly-1.scala b/tests/run/phantom-poly-1.scala new file mode 100644 index 000000000000..6c6f72d86580 --- /dev/null +++ b/tests/run/phantom-poly-1.scala @@ -0,0 +1,23 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + polyfun1() + polyfun1[Casper]() + } + + def polyfun1[P <: Casper](): Unit = { + println("polyfun1") + } + +} + +object Boo extends Phantom { + type Casper <: this.Any + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/run/phantom-poly-2.check b/tests/run/phantom-poly-2.check new file mode 100644 index 000000000000..91de48245c31 --- /dev/null +++ b/tests/run/phantom-poly-2.check @@ -0,0 +1,3 @@ +polyfun2 +polyfun2 +polyfun2 diff --git a/tests/run/phantom-poly-2.scala b/tests/run/phantom-poly-2.scala new file mode 100644 index 000000000000..e96e17f913cb --- /dev/null +++ b/tests/run/phantom-poly-2.scala @@ -0,0 +1,25 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + polyfun2(boo[Blinky]) + polyfun2(boo[Inky]) + polyfun2(boo[Pinky]) + } + + def polyfun2[G <: Blinky](p: G): Unit = { + println("polyfun2") + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: this.Any]: B = assume[B] +} diff --git a/tests/run/phantom-poly-3.check b/tests/run/phantom-poly-3.check new file mode 100644 index 000000000000..6bad8615924c --- /dev/null +++ b/tests/run/phantom-poly-3.check @@ -0,0 +1,3 @@ +polyfun3 +polyfun3 +polyfun3 diff --git a/tests/run/phantom-poly-3.scala b/tests/run/phantom-poly-3.scala new file mode 100644 index 000000000000..a21b1c544827 --- /dev/null +++ b/tests/run/phantom-poly-3.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + polyfun3(boo[Blinky]) + polyfun3(boo[Inky]) + polyfun3(boo[Pinky]) + } + + def polyfun3[G <: BooAny, I <: G](q: I): Unit = { + println("polyfun3") + } +} + +object Boo extends Phantom { + type BooAny = this.Any + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-poly-4.check b/tests/run/phantom-poly-4.check new file mode 100644 index 000000000000..db5561ee606b --- /dev/null +++ b/tests/run/phantom-poly-4.check @@ -0,0 +1,3 @@ +polyfun4 +polyfun4 +polyfun4 diff --git a/tests/run/phantom-poly-4.scala b/tests/run/phantom-poly-4.scala new file mode 100644 index 000000000000..4502da5619eb --- /dev/null +++ b/tests/run/phantom-poly-4.scala @@ -0,0 +1,26 @@ +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + polyfun4(boo[Blinky]) + polyfun4(boo[Inky]) + polyfun4(boo[Pinky]) + } + + def polyfun4[P >: BooNothing](p: P): Unit = { + println("polyfun4") + } +} + +object Boo extends Phantom { + type BooNothing = Boo.Nothing + type Blinky <: this.Any + type Inky <: Blinky + type Pinky <: Inky + def boo[B <: Blinky]: B = assume[B] +} diff --git a/tests/run/phantom-val.check b/tests/run/phantom-val.check new file mode 100644 index 000000000000..06b2967cef34 --- /dev/null +++ b/tests/run/phantom-val.check @@ -0,0 +1,2 @@ +foo +1 diff --git a/tests/run/phantom-val.scala b/tests/run/phantom-val.scala new file mode 100644 index 000000000000..455c188026b4 --- /dev/null +++ b/tests/run/phantom-val.scala @@ -0,0 +1,26 @@ + +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + println(1) + foo + foo + + } + + val foo = { + println("foo") + any + } +} + +object Boo extends Phantom { + type BooAny = this.Any + def any: BooAny = assume[BooAny] +} diff --git a/tests/run/phantom-var.check b/tests/run/phantom-var.check new file mode 100644 index 000000000000..b210800439ff --- /dev/null +++ b/tests/run/phantom-var.check @@ -0,0 +1,2 @@ +foo +foo2 diff --git a/tests/run/phantom-var.scala b/tests/run/phantom-var.scala new file mode 100644 index 000000000000..1f0aae4a686b --- /dev/null +++ b/tests/run/phantom-var.scala @@ -0,0 +1,30 @@ + +/* Run this test with + * `run tests/run/xyz.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomTermErasure,phantomTypeErasure,erasure` + * to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure. + */ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + foo + foo + foo = { + println("foo2") + any + } + foo + + } + + var foo = { + println("foo") + any + } +} + +object Boo extends Phantom { + type BooAny = this.Any + def any: BooAny = assume[BooAny] +}