From f067b8e4a9d333076e1fddfbd96fe2790cb526ca Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 23 Jul 2016 21:38:19 +0200 Subject: [PATCH 01/32] Rename old PhantomClasses to avoid name clashes with phantom types. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Mixin.scala | 2 +- compiler/src/dotty/tools/dotc/transform/TreeGen.scala | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 0920ca946598..2871ba2df291 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -820,7 +820,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 @@ -832,7 +832,7 @@ class Definitions { * trait gets screwed up. Therefore, it is mandatory that FunctionXXL * is treated as a NoInit trait. */ - lazy val NoInitClasses = PhantomClasses + FunctionXXLClass + lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass def isPolymorphicAfterErasure(sym: Symbol) = (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e01dab87228c..b3a386914bf9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -483,8 +483,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/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 683c8ac38a6c..9a768e464483 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/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 546077d279a2..f90778df009a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -174,7 +174,7 @@ 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 else { 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 } From ac57124129062218170b0c290d7b2b8ce66de8f7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 19 Jul 2016 11:22:51 +0200 Subject: [PATCH 02/32] Implement phantom types. In this commit phantom types are defined and implemented. Only a minimal version of phantom term erasure is included. Phantom types are in latices outside of the normal `Any`/`Nothing` type lattice. A new phantom lattice can be defined by extending an `object` with `scala.Phantom`. This trait defines synthetic members * `protected final trait Any` where this `Any` does not extends `scala.Any` * `protected final abstract class Nothing extends this.Any` * `protected final def assume[P >: this.Nothing <: this.Any]: P` A phantom lattice object can expose any of the phantom members (`Any`, `Nothing`, `assume`) using an alias but it is not required. Restriction on lattices: * Types from different lattice can not be mixed using `&` or `|` types. * Type parameters must be bounded by types in a single lattice. Phantom type erasure (minimalistic): * Type references `Phantom.Any` and `Phantom.Nothing` are erased to `dotty.runtime.ErasedPhantom` * `Phantom.assume[P]` is erased to `null.asInstanceOf[P]` * `Phantom` is erased to `dotty.runtime.ErasedPhantomLattice`, removing the members of `Phantom` --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 + .../src/dotty/tools/dotc/ast/Desugar.scala | 10 -- .../dotty/tools/dotc/core/Definitions.scala | 56 ++++++++-- .../src/dotty/tools/dotc/core/StdNames.scala | 2 + .../dotty/tools/dotc/core/TypeComparer.scala | 13 ++- .../src/dotty/tools/dotc/core/TypeOps.scala | 7 +- .../src/dotty/tools/dotc/core/Types.scala | 14 ++- .../reporting/diagnostic/ErrorMessageID.java | 8 ++ .../dotc/reporting/diagnostic/messages.scala | 80 +++++++++++++- .../phantom/PhantomTypeErasure.scala | 55 ++++++++++ .../src/dotty/tools/dotc/typer/Typer.scala | 49 ++++++++- library/src/dotty/runtime/ErasedPhantom.scala | 3 + .../dotty/runtime/ErasedPhantomLattice.scala | 3 + library/src/scala/Phantom.scala | 13 +++ tests/neg/phantom-AndOr.scala | 18 ++++ tests/neg/phantom-Eq.scala | 39 +++++++ tests/neg/phantom-classOf-1.scala | 10 ++ tests/neg/phantom-classOf-2.scala | 10 ++ tests/neg/phantom-evidence.scala | 37 +++++++ tests/neg/phantom-expr.scala | 23 ++++ tests/neg/phantom-fun-app.scala | 24 +++++ tests/neg/phantom-instanceOf-1.scala | 11 ++ tests/neg/phantom-instanceOf-2.scala | 14 +++ tests/neg/phantom-multiversal-AndOr.scala | 21 ++++ ...ntom-multiversal-type-param-bounds-1.scala | 18 ++++ ...ntom-multiversal-type-param-bounds-2.scala | 22 ++++ tests/neg/phantom-multiversal.scala | 35 ++++++ tests/neg/phantom-trait-1.scala | 4 + tests/neg/phantom-trait-2.scala | 5 + tests/neg/phantom-trait-3.scala | 6 ++ tests/neg/phantom-type-param-bounds-1.scala | 9 ++ tests/neg/phantom-type-param-bounds-2.scala | 13 +++ tests/pos/phantom-Eq.scala | 42 ++++++++ tests/pos/phantom-Eq2/Phantom-Eq_1.scala | 20 ++++ tests/pos/phantom-Eq2/Phantom-Eq_2.scala | 15 +++ tests/pos/phantom-Evidence.scala | 38 +++++++ tests/run/phantom-1.check | 1 + tests/run/phantom-1.scala | 22 ++++ tests/run/phantom-2.check | 1 + tests/run/phantom-2.scala | 20 ++++ tests/run/phantom-3.check | 3 + tests/run/phantom-3.scala | 26 +++++ tests/run/phantom-4.check | 3 + tests/run/phantom-4.scala | 27 +++++ tests/run/phantom-5.check | 3 + tests/run/phantom-5.scala | 27 +++++ tests/run/phantom-ImplicitFunctions-0.check | 0 tests/run/phantom-OnHList.check | 4 + tests/run/phantom-OnHList.scala | 100 ++++++++++++++++++ tests/run/phantom-decls-1.check | 4 + tests/run/phantom-decls-1.scala | 29 +++++ tests/run/phantom-decls-2.check | 6 ++ tests/run/phantom-decls-2.scala | 31 ++++++ tests/run/phantom-decls-3.check | 4 + tests/run/phantom-decls-3.scala | 36 +++++++ tests/run/phantom-decls-4.check | 3 + tests/run/phantom-decls-4.scala | 25 +++++ tests/run/phantom-decls-5.check | 2 + tests/run/phantom-decls-5.scala | 24 +++++ tests/run/phantom-hk-1.check | 3 + tests/run/phantom-hk-1.scala | 31 ++++++ tests/run/phantom-hk-2.check | 3 + tests/run/phantom-hk-2.scala | 29 +++++ tests/run/phantom-methods-1.check | 0 tests/run/phantom-methods-1.scala | 22 ++++ tests/run/phantom-methods-2.check | 3 + tests/run/phantom-methods-2.scala | 26 +++++ tests/run/phantom-methods-3.check | 3 + tests/run/phantom-methods-3.scala | 26 +++++ tests/run/phantom-methods-4.check | 3 + tests/run/phantom-methods-4.scala | 26 +++++ tests/run/phantom-methods-5.check | 3 + tests/run/phantom-methods-5.scala | 26 +++++ tests/run/phantom-methods-6.check | 1 + tests/run/phantom-methods-6.scala | 24 +++++ tests/run/phantom-methods-7.check | 2 + tests/run/phantom-methods-7.scala | 24 +++++ tests/run/phantom-poly-1.check | 2 + tests/run/phantom-poly-1.scala | 23 ++++ tests/run/phantom-poly-2.check | 3 + tests/run/phantom-poly-2.scala | 25 +++++ tests/run/phantom-poly-3.check | 3 + tests/run/phantom-poly-3.scala | 26 +++++ tests/run/phantom-poly-4.check | 3 + tests/run/phantom-poly-4.scala | 26 +++++ 85 files changed, 1489 insertions(+), 27 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala create mode 100644 library/src/dotty/runtime/ErasedPhantom.scala create mode 100644 library/src/dotty/runtime/ErasedPhantomLattice.scala create mode 100644 library/src/scala/Phantom.scala create mode 100644 tests/neg/phantom-AndOr.scala create mode 100644 tests/neg/phantom-Eq.scala create mode 100644 tests/neg/phantom-classOf-1.scala create mode 100644 tests/neg/phantom-classOf-2.scala create mode 100644 tests/neg/phantom-evidence.scala create mode 100644 tests/neg/phantom-expr.scala create mode 100644 tests/neg/phantom-fun-app.scala create mode 100644 tests/neg/phantom-instanceOf-1.scala create mode 100644 tests/neg/phantom-instanceOf-2.scala create mode 100644 tests/neg/phantom-multiversal-AndOr.scala create mode 100644 tests/neg/phantom-multiversal-type-param-bounds-1.scala create mode 100644 tests/neg/phantom-multiversal-type-param-bounds-2.scala create mode 100644 tests/neg/phantom-multiversal.scala create mode 100644 tests/neg/phantom-trait-1.scala create mode 100644 tests/neg/phantom-trait-2.scala create mode 100644 tests/neg/phantom-trait-3.scala create mode 100644 tests/neg/phantom-type-param-bounds-1.scala create mode 100644 tests/neg/phantom-type-param-bounds-2.scala create mode 100644 tests/pos/phantom-Eq.scala create mode 100644 tests/pos/phantom-Eq2/Phantom-Eq_1.scala create mode 100644 tests/pos/phantom-Eq2/Phantom-Eq_2.scala create mode 100644 tests/pos/phantom-Evidence.scala create mode 100644 tests/run/phantom-1.check create mode 100644 tests/run/phantom-1.scala create mode 100644 tests/run/phantom-2.check create mode 100644 tests/run/phantom-2.scala create mode 100644 tests/run/phantom-3.check create mode 100644 tests/run/phantom-3.scala create mode 100644 tests/run/phantom-4.check create mode 100644 tests/run/phantom-4.scala create mode 100644 tests/run/phantom-5.check create mode 100644 tests/run/phantom-5.scala create mode 100644 tests/run/phantom-ImplicitFunctions-0.check create mode 100644 tests/run/phantom-OnHList.check create mode 100644 tests/run/phantom-OnHList.scala create mode 100644 tests/run/phantom-decls-1.check create mode 100644 tests/run/phantom-decls-1.scala create mode 100644 tests/run/phantom-decls-2.check create mode 100644 tests/run/phantom-decls-2.scala create mode 100644 tests/run/phantom-decls-3.check create mode 100644 tests/run/phantom-decls-3.scala create mode 100644 tests/run/phantom-decls-4.check create mode 100644 tests/run/phantom-decls-4.scala create mode 100644 tests/run/phantom-decls-5.check create mode 100644 tests/run/phantom-decls-5.scala create mode 100644 tests/run/phantom-hk-1.check create mode 100644 tests/run/phantom-hk-1.scala create mode 100644 tests/run/phantom-hk-2.check create mode 100644 tests/run/phantom-hk-2.scala create mode 100644 tests/run/phantom-methods-1.check create mode 100644 tests/run/phantom-methods-1.scala create mode 100644 tests/run/phantom-methods-2.check create mode 100644 tests/run/phantom-methods-2.scala create mode 100644 tests/run/phantom-methods-3.check create mode 100644 tests/run/phantom-methods-3.scala create mode 100644 tests/run/phantom-methods-4.check create mode 100644 tests/run/phantom-methods-4.scala create mode 100644 tests/run/phantom-methods-5.check create mode 100644 tests/run/phantom-methods-5.scala create mode 100644 tests/run/phantom-methods-6.check create mode 100644 tests/run/phantom-methods-6.scala create mode 100644 tests/run/phantom-methods-7.check create mode 100644 tests/run/phantom-methods-7.scala create mode 100644 tests/run/phantom-poly-1.check create mode 100644 tests/run/phantom-poly-1.scala create mode 100644 tests/run/phantom-poly-2.check create mode 100644 tests/run/phantom-poly-2.scala create mode 100644 tests/run/phantom-poly-3.check create mode 100644 tests/run/phantom-poly-3.scala create mode 100644 tests/run/phantom-poly-4.check create mode 100644 tests/run/phantom-poly-4.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 8ee016117de3..3cac60c32cb9 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 @@ -76,6 +77,7 @@ class Compiler { 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. + 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 diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 34a4cec0d8fc..2594d95df66b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -670,16 +670,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 2871ba2df291..6c51c3a1a750 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -149,17 +149,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 = PolyType.syntheticParamNames(typeParamCount) - val tparamInfos = tparamNames map (_ => TypeBounds.empty) + val tparamInfos = tparamNames map bounds val ptype = PolyType(tparamNames)(_ => tparamInfos, 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] = { @@ -927,7 +928,7 @@ class Definitions { // ----- Initialization --------------------------------------------------- /** 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, @@ -936,14 +937,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 @@ -963,4 +965,44 @@ 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 = tp.phantomTopClass match { + case top: ClassInfo => top.prefix.select(tpnme.Any) + case _ => defn.AnyType + } + + def bottomOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match { + case top: ClassInfo => top.prefix.select(tpnme.Nothing) + case _ => 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/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index a7f658849dd8..60c6a6ed06c0 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -237,6 +237,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/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 36c134a95d2f..c53b8a6eb89d 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.phantomTopClass == tp2.phantomTopClass) + 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 fa06446d739e..bb4597faa259 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -16,6 +16,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 @@ -204,7 +206,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) @@ -260,7 +262,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 637239db038c..c0c6a60bd76e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -172,9 +172,21 @@ object Types { case _ => false } - cls == defn.AnyClass || loop(this) + loop(this) + } + + final def isPhantom(implicit ctx: Context): Boolean = phantomTopClass.exists + + final def phantomTopClass(implicit ctx: Context): Type = this match { + case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp + case tp: TypeProxy => tp.superType.phantomTopClass + case tp: AndOrType => tp.tp1.phantomTopClass + case _ => NoType } + private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = + sym.isClass && (sym.owner eq defn.PhantomClass) && (sym.name == tpnme.Any || sym.name == tpnme.Nothing) + /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could * be refined later. diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 73bec87c01df..59a4fca6ced3 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -57,6 +57,14 @@ public enum ErrorMessageID { CyclicReferenceInvolvingImplicitID, SuperQualMustBeParentID, AmbiguousImportID, + 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 aa70443db42e..a3f482b1bdb4 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} @@ -135,7 +136,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 @@ -1252,5 +1253,82 @@ object messages { | ${"import"} scala.{ $name => ${name.show + "Tick"} } |""" } + + 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/phantom/PhantomTypeErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala new file mode 100644 index 000000000000..43d274521868 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala @@ -0,0 +1,55 @@ +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.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" + + /** 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/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ca9e0d126689..7cb51ed0a4b2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -651,6 +651,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) } @@ -822,6 +824,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 = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen val cases1 = typedCases(tree.cases, selType, pt.notApplied) @@ -852,7 +856,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 @@ -1108,10 +1118,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 @@ -1330,6 +1344,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) @@ -1631,8 +1648,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) 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-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/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-ImplicitFunctions-0.check b/tests/run/phantom-ImplicitFunctions-0.check new file mode 100644 index 000000000000..e69de29bb2d1 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-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-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-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-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] +} From 84931e3ef869c6e1628b79f5ab18f799d8b58bf0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 22 Mar 2017 13:47:20 +0100 Subject: [PATCH 03/32] Extra tests for Phantoms 1. --- tests/run/phantom-methods-10.check | 3 +++ tests/run/phantom-methods-10.scala | 31 ++++++++++++++++++++++++++++++ tests/run/phantom-methods-8.check | 2 ++ tests/run/phantom-methods-8.scala | 26 +++++++++++++++++++++++++ tests/run/phantom-methods-9.check | 3 +++ tests/run/phantom-methods-9.scala | 31 ++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 tests/run/phantom-methods-10.check create mode 100644 tests/run/phantom-methods-10.scala create mode 100644 tests/run/phantom-methods-8.check create mode 100644 tests/run/phantom-methods-8.scala create mode 100644 tests/run/phantom-methods-9.check create mode 100644 tests/run/phantom-methods-9.scala 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-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] +} From a23ef920bb9f04fb41d8bae7ceaff3314e03e6f7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 22 Mar 2017 13:51:57 +0100 Subject: [PATCH 04/32] Extra tests for Phantoms 2. --- tests/run/phantom-lazy-val.check | 3 +++ tests/run/phantom-lazy-val.scala | 27 +++++++++++++++++++++++++++ tests/run/phantom-val.check | 2 ++ tests/run/phantom-val.scala | 26 ++++++++++++++++++++++++++ tests/run/phantom-var.check | 2 ++ tests/run/phantom-var.scala | 30 ++++++++++++++++++++++++++++++ 6 files changed, 90 insertions(+) create mode 100644 tests/run/phantom-lazy-val.check create mode 100644 tests/run/phantom-lazy-val.scala create mode 100644 tests/run/phantom-val.check create mode 100644 tests/run/phantom-val.scala create mode 100644 tests/run/phantom-var.check create mode 100644 tests/run/phantom-var.scala 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-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] +} From 7ccdbf3cc3248c2c44344eeeb7b7084f77c7d5b3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 09:00:01 +0200 Subject: [PATCH 05/32] Quick cleanup --- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index bb4597faa259..5518dbb21d33 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -206,7 +206,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } /** The minimal set of classes in `cs` which derive all other classes in `cs` */ - @tailrec def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = cs match { + def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match { case c :: rest => val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu if (cs == c.baseClasses) accu1 else dominators(rest, accu1) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7cb51ed0a4b2..3fb15f7c75a2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -651,7 +651,7 @@ 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) + 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) @@ -1651,7 +1651,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit 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, + /** Check that there 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] = { From 01063d37619d0536b661fff1b3d2ebead657f881 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 10:08:32 +0200 Subject: [PATCH 06/32] Optimizations --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6c51c3a1a750..4a2cc3971a95 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -982,13 +982,13 @@ class Definitions { } def isPhantomAnyClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.exists && (sym.owner eq PhantomClass) && sym.name == tpnme.Any + sym.name == tpnme.Any && (sym.owner eq PhantomClass) def isPhantomNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.exists && (sym.owner eq PhantomClass) && sym.name == tpnme.Nothing + sym.name == tpnme.Nothing && (sym.owner eq PhantomClass) def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean = - sym.exists && (sym.owner eq PhantomClass) && sym.name == nme.assume_ + sym.name == nme.assume_ && (sym.owner eq PhantomClass) def topOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match { case top: ClassInfo => top.prefix.select(tpnme.Any) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c0c6a60bd76e..dc2e4d97ad42 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -185,7 +185,7 @@ object Types { } private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.isClass && (sym.owner eq defn.PhantomClass) && (sym.name == tpnme.Any || sym.name == tpnme.Nothing) + sym.isClass && (sym.owner eq defn.PhantomClass) /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could From 5fb733c4741d64bf1eddbea4d1b8d7ea84f37a26 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 10:22:46 +0200 Subject: [PATCH 07/32] Remove unecessary code --- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 5518dbb21d33..fa06446d739e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -16,8 +16,6 @@ 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 @@ -262,8 +260,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else tp.baseTypeWithArgs(cls) base.mapReduceOr(identity)(mergeRefined) } - 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) + doms.map(baseTp).reduceLeft(AndType.apply) } } } From e8590f6c293c4a7fe4ece712e0275557dc6ce6f8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 10:36:34 +0200 Subject: [PATCH 08/32] Add restriction on phantom latice definition. --- .../reporting/diagnostic/ErrorMessageID.java | 1 + .../dotc/reporting/diagnostic/messages.scala | 3 +-- .../src/dotty/tools/dotc/typer/Typer.scala | 8 +++++-- tests/neg/phantom-trait-4.scala | 23 +++++++++++++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/neg/phantom-trait-4.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 59a4fca6ced3..427b44468c39 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -65,6 +65,7 @@ public enum ErrorMessageID { MatchOnPhantomID, IfElsePhantomID, PhantomIsInObjectID, + PhantomObjectIsInPackageOrObjectID, ; 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 a3f482b1bdb4..94702052636c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1329,6 +1329,5 @@ object messages { val explanation = hl"""|""".stripMargin - } - + } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3fb15f7c75a2..78ec76bb06e6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1344,8 +1344,12 @@ 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) + if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass)) { + if (!cls.is(Module)) + ctx.error(PhantomIsInObject(), cdef.pos) + else if (!cls.owner.is(Module) && !cls.owner.is(Package)) + ctx.error(PhantomObjectIsInPackageOrObject(), cdef.pos) + } // check value class constraints checkDerivedValueClass(cls, body1) diff --git a/tests/neg/phantom-trait-4.scala b/tests/neg/phantom-trait-4.scala new file mode 100644 index 000000000000..19a7e7b670d1 --- /dev/null +++ b/tests/neg/phantom-trait-4.scala @@ -0,0 +1,23 @@ + +class Foo { + + object Boo1 extends Phantom // error + + def foo = { + object Boo2 extends Phantom // error + 42 + } +} + +object Foo { + object Boo1 extends Phantom + + def foo = { + object Boo2 extends Phantom // error + 42 + } +} + +package foo { + object Boo1 extends Phantom +} From 621a67b5e7238ea7c866dbe4e0fd59923b229066 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 20:57:39 +0200 Subject: [PATCH 09/32] Fixes after rebase. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++-- .../tools/dotc/transform/phantom/PhantomTypeErasure.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4a2cc3971a95..3a80c58f7697 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -974,8 +974,8 @@ class Definitions { 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)) + val tparamNames = List("P".toTypeName) + val ptype = PolyType(tparamNames)(_ => TypeBounds(nothing.typeRef, any.typeRef) :: Nil, TypeParamRef(_, 0)) newSymbol(cls, nme.assume_, Protected | Final | Method, ptype).entered cls diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala index 43d274521868..280b23686e83 100644 --- a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala @@ -45,7 +45,7 @@ class PhantomTypeErasure extends MiniPhaseTransform with InfoTransformer { 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) + methodType(tp.paramNames, tp.paramInfos, defn.ObjectType) case _ => mapOver(tp) } } From ee447318d63d3f4f8795d9ad932b76ddba340e3a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 21:49:42 +0200 Subject: [PATCH 10/32] Replace phantomTop by general top type. --- .../dotty/tools/dotc/core/Definitions.scala | 10 ------ .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 32 ++++++++++++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 6 ++-- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3a80c58f7697..adf9cf159327 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -990,16 +990,6 @@ class Definitions { def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean = sym.name == nme.assume_ && (sym.owner eq PhantomClass) - def topOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match { - case top: ClassInfo => top.prefix.select(tpnme.Any) - case _ => defn.AnyType - } - - def bottomOf(tp: Type)(implicit ctx: Context): Type = tp.phantomTopClass match { - case top: ClassInfo => top.prefix.select(tpnme.Nothing) - case _ => defn.NothingType - } - lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") def ErasedPhantomType = ErasedPhantomClass.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c53b8a6eb89d..b55190ca93e2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -559,7 +559,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } 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.phantomTopClass == tp2.phantomTopClass) + else if (defn.isPhantomNothingClass(tp1.symbol)) tp2.isValueTypeOrLambda && (tp1.topType == tp2.topType) else false } case tp1: SingletonType => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index dc2e4d97ad42..416a63f9cbbd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -175,15 +175,31 @@ object Types { loop(this) } - final def isPhantom(implicit ctx: Context): Boolean = phantomTopClass.exists + /** Is phantom if upper bounded by XYZ.Any where XYZ extends scala.Phantom */ + final def isPhantom(implicit ctx: Context): Boolean = isPhantomClass(topType.classSymbol) - final def phantomTopClass(implicit ctx: Context): Type = this match { - case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp - case tp: TypeProxy => tp.superType.phantomTopClass - case tp: AndOrType => tp.tp1.phantomTopClass - case _ => NoType + /** Returns the top type of the lattice + * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any + * - scala.Any otherwise + */ + final def topType(implicit ctx: Context): TypeRef = this match { + case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp.prefix.select(tpnme.Any).asInstanceOf[TypeRef] + case tp: TypeProxy => tp.superType.topType + case tp: AndOrType => tp.tp1.topType + case _ => defn.AnyType + } + + /** Returns the bottom type of the lattice + * - XYZ.Nothing if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any + * - scala.Nothing otherwise + */ + final def bottomType(implicit ctx: Context): Type = topType match { + case top: TypeRef if top.prefix.termSymbol ne defn.ScalaPackageVal => + top.prefix.select(tpnme.Nothing) + case _ => defn.NothingType } + /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass && (sym.owner eq defn.PhantomClass) @@ -3287,8 +3303,8 @@ object Types { /** Type bounds >: lo <: hi */ abstract case class TypeBounds(lo: Type, hi: Type) extends CachedProxyType with TypeType { - assert(lo.isInstanceOf[TermType]) - assert(hi.isInstanceOf[TermType]) + assert(lo.isInstanceOf[TermType], lo) + assert(hi.isInstanceOf[TermType], hi) def variance: Int = 0 diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 78ec76bb06e6..c310c8d2cddf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1122,8 +1122,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val lo1 = typed(lo) val hi1 = typed(hi) - 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 lo2 = if (!lo1.isEmpty) lo1 else typed(untpd.TypeTree(hi1.typeOpt.bottomType)) + val hi2 = if (!hi1.isEmpty) hi1 else typed(untpd.TypeTree(lo1.typeOpt.topType)) val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo2, hi2), lo2, hi2) if (ctx.mode.is(Mode.Pattern)) { @@ -1671,7 +1671,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case untpd.InfixOp(left, op, right) => checkedTops2(left, right, PhantomCrossedMixedBounds(left, right), tree.pos) case EmptyTree => Set.empty - case _ => Set(defn.topOf(tree.typeOpt)) + case _ => Set(tree.typeOpt.topType) } } checkedTops(tree) From 0f4f863d9b3a0477c810de21afdfc233ec2b6860 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 6 Apr 2017 22:33:26 +0200 Subject: [PATCH 11/32] Fix error on if/match with phantoms of different lattices. --- .../dotc/reporting/diagnostic/messages.scala | 12 +++++--- .../src/dotty/tools/dotc/typer/Typer.scala | 12 ++++---- tests/neg/phantom-expr.scala | 30 ++++++++++++++++--- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 94702052636c..63654a33f328 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1298,9 +1298,9 @@ object messages { hl"""|""".stripMargin } - case class MatchPhantom()(implicit ctx: Context) extends Message(MatchPhantomID) { + case class MatchPhantom(tpdCase: tpd.CaseDef, expected: Type)(implicit ctx: Context) extends Message(MatchPhantomID) { val kind = "Phantom restriction" - val msg = "Pattern matches cannot return phantom and non phantoms" + val msg = s"Pattern expected case to return a ${expected.show} but was ${tpdCase.tpe.show}" val explanation = hl"""|""".stripMargin @@ -1315,9 +1315,13 @@ object messages { hl"""|""".stripMargin } - case class IfElsePhantom()(implicit ctx: Context) extends Message(IfElsePhantomID) { + case class IfElsePhantom(thenp: tpd.Tree, elsep: tpd.Tree)(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 msg = + s"""if/else cannot have branches with types in different lattices: + | ${thenp.tpe.show} of lattice ${thenp.tpe.topType.show} + | ${elsep.tpe.show} of lattice ${elsep.tpe.topType.show} + """.stripMargin val explanation = hl"""|""".stripMargin diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c310c8d2cddf..76204de933e9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -651,8 +651,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) + if (thenp1.tpe.topType != elsep1.tpe.topType) + ctx.error(IfElsePhantom(thenp1, elsep1), tree.pos) val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } @@ -858,9 +858,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit 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) + if (tpdCases.nonEmpty) { + val top = tpdCases.head.tpe.topType + for (tpdCase <- tpdCases if tpdCase.tpe.topType != top) + ctx.error(MatchPhantom(tpdCase, top), tpdCase.pos) + } tpdCases } diff --git a/tests/neg/phantom-expr.scala b/tests/neg/phantom-expr.scala index c961dedecc21..7801c05c0a90 100644 --- a/tests/neg/phantom-expr.scala +++ b/tests/neg/phantom-expr.scala @@ -1,6 +1,7 @@ class Foo { import Boo._ + import Boo1._ type Blinky <: BooAny type Inky <: BooAny @@ -9,11 +10,28 @@ class Foo { val inky = Boo.boo[Inky] val b = true - def fooIf1 = if (b) { blinky } else { "" } // error - def fooIf2 = if (b) { "" } else { blinky } // error + def fooIf1 = + if (b) blinky // error + else "" - def fooMatch1 = blinky match { case _: Blinky => () } // error - def fooMatch2 = 1 match { case 1 => 2 case _ => blinky } // error + def fooIf2 = + if (b) "" // error + else blinky + + def fooIf3 = + if (b) boo1 // error + else blinky + + def fooMatch1 = blinky match { // error + case _: Blinky => () + } + def fooMatch2 = 1 match { case 1 => 2 + case _ => blinky // error + } + def fooMatch3 = 1 match { + case 1 => boo1 + case _ => blinky // error + } } object Boo extends Phantom { @@ -21,3 +39,7 @@ object Boo extends Phantom { def boo[B <: BooAny]: B = assume[B] } +object Boo1 extends Phantom { + type Boo1Any = this.Any + def boo1: Boo1Any = assume[Boo1Any] +} \ No newline at end of file From efae3a30d8f1c360ef30b0cf98b45d0e803e9a39 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sat, 8 Apr 2017 18:21:24 +0200 Subject: [PATCH 12/32] Inline error messages. --- .../dotc/reporting/diagnostic/messages.scala | 1 - .../src/dotty/tools/dotc/typer/Typer.scala | 38 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 63654a33f328..7bfa9c461725 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -10,7 +10,6 @@ import Symbols._ import Names._ import NameOps._ import Types._ -import Flags._ import util.SourcePosition import config.Settings.Setting import interfaces.Diagnostic.{ERROR, INFO, WARNING} diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 76204de933e9..565f34f95d0b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -651,8 +651,12 @@ 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.topType != elsep1.tpe.topType) - ctx.error(IfElsePhantom(thenp1, elsep1), tree.pos) + if (thenp1.tpe.topType != elsep1.tpe.topType) { + ctx.error(s"""if/else cannot have branches with types in different lattices: + | ${thenp1.tpe.show} of lattice ${thenp1.tpe.topType.show} + | ${elsep1.tpe.show} of lattice ${elsep1.tpe.topType.show} + """.stripMargin, tree.pos) + } val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } @@ -825,7 +829,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => val sel1 = typedExpr(tree.selector) if (sel1.tpe.isPhantom) - ctx.error(MatchOnPhantom(), sel1.pos) + ctx.error("Cannot pattern match on phantoms", sel1.pos) val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen val cases1 = typedCases(tree.cases, selType, pt.notApplied) @@ -861,7 +865,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tpdCases.nonEmpty) { val top = tpdCases.head.tpe.topType for (tpdCase <- tpdCases if tpdCase.tpe.topType != top) - ctx.error(MatchPhantom(tpdCase, top), tpdCase.pos) + ctx.error(s"Pattern expected case to return a ${top.show} but was ${tpdCase.tpe.show}", tpdCase.pos) } tpdCases @@ -1348,9 +1352,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass)) { if (!cls.is(Module)) - ctx.error(PhantomIsInObject(), cdef.pos) + ctx.error("Only object can extend scala.Phantom", cdef.pos) else if (!cls.owner.is(Module) && !cls.owner.is(Package)) - ctx.error(PhantomObjectIsInPackageOrObject(), cdef.pos) + ctx.error("An object extending scala.Phantom must be a top level object or in another object.", cdef.pos) } // check value class constraints @@ -1661,17 +1665,23 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * 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) + val allTops = checkedTops(hi) union checkedTops(lo) + if (allTops.size <= 1) allTops + else { + ctx.error("Type can not be bounded at the same time by types in different latices: " + + allTops.map(_.show).mkString(", "), tree.pos) + Set.empty + } case untpd.InfixOp(left, op, right) => - checkedTops2(left, right, PhantomCrossedMixedBounds(left, right), tree.pos) + val allTops = checkedTops(left) union checkedTops(right) + if (allTops.size <= 1) allTops + else { + ctx.error(s"Can not use ${op.show} mix types of different lattices: " + + allTops.map(_.show).mkString(", "), tree.pos) + Set.empty + } case EmptyTree => Set.empty case _ => Set(tree.typeOpt.topType) } From 510c0cb80c5b5bdd6aaf3f3f06c59011b7317107 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 9 Apr 2017 22:00:45 +0200 Subject: [PATCH 13/32] Fix phantom Nothing. --- .../dotty/tools/dotc/core/TypeComparer.scala | 10 ++------ .../phantom/PhantomTypeErasure.scala | 9 ++++--- tests/neg/phantom-bottom.scala | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 tests/neg/phantom-bottom.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b55190ca93e2..da37332c0cb3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -551,14 +551,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false } - 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) + if (tp1.symbol eq NothingClass) tp2.isValueTypeOrLambda && (tp2.topType.classSymbol eq AnyClass) + else if (tp1.symbol eq NullClass) isNullable(tp2) && (tp2.topType.classSymbol eq AnyClass) else if (defn.isPhantomNothingClass(tp1.symbol)) tp2.isValueTypeOrLambda && (tp1.topType == tp2.topType) else false } diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala index 280b23686e83..73d19c689f24 100644 --- a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala @@ -38,10 +38,11 @@ class PhantomTypeErasure extends MiniPhaseTransform with InfoTransformer { 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: TypeRef => + val sym = tp.classSymbol + if (defn.isPhantomAnyClass(sym) || defn.isPhantomNothingClass(sym)) defn.ErasedPhantomType + else if (sym eq defn.PhantomClass) defn.ErasedPhantomLatticeType + else mapOver(tp) 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 diff --git a/tests/neg/phantom-bottom.scala b/tests/neg/phantom-bottom.scala new file mode 100644 index 000000000000..6d54c7342153 --- /dev/null +++ b/tests/neg/phantom-bottom.scala @@ -0,0 +1,25 @@ + +class BooFunDef1 { + import Boo._ + + def fun0(x: Foo): x.Y = Boo.nothing + + def fun1(x: Foo): x.Y = ??? // error + def fun2(x: Foo): x.Y = null // error + def fun3(x: Foo): x.Y = Boo2.nothing // error +} + +class Foo { + type Y <: Boo.BooAny +} + +object Boo extends Phantom { + type BooAny = this.Any + type BooNothing = this.Nothing + def nothing: BooNothing = assume[BooNothing] +} + +object Boo2 extends Phantom { + type BooNothing2 = this.Nothing + def nothing: BooNothing2 = assume[BooNothing2] +} \ No newline at end of file From f78228816dd7981cef2ff7a399c58357453eeae0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 9 Apr 2017 23:08:08 +0200 Subject: [PATCH 14/32] Fix cyclic computation of topType. neg/cycles.scala --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 416a63f9cbbd..534b7ba1308f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -182,9 +182,9 @@ object Types { * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - scala.Any otherwise */ - final def topType(implicit ctx: Context): TypeRef = this match { + final def topType(implicit ctx: Context): TypeRef = widen match { case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp.prefix.select(tpnme.Any).asInstanceOf[TypeRef] - case tp: TypeProxy => tp.superType.topType + case tp: TypeProxy if tp.superType ne this => tp.superType.topType case tp: AndOrType => tp.tp1.topType case _ => defn.AnyType } From 341cdf14493da3b8b4fbea52dd9b0aef7f975c0f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 9 Apr 2017 23:43:03 +0200 Subject: [PATCH 15/32] Move phantom universe checks from Typer to TypeAssigner. --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 31 ++++++++++-- .../src/dotty/tools/dotc/typer/Typer.scala | 50 +------------------ 2 files changed, 28 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 4496c2bcf1c7..ec0fbeda7208 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -417,8 +417,10 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { + checkSameUniverse(thenp, elsep, "be combined in branches of if/else", tree.pos) tree.withType(thenp.tpe | elsep.tpe) + } def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = tree.withType( @@ -428,8 +430,15 @@ trait TypeAssigner { def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = tree.withType(body.tpe) - def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = + def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { + if (tree.selector.typeOpt.isPhantom) + ctx.error("Cannot pattern match on phantoms", tree.selector.pos) + if (cases.nonEmpty) { + val head = cases.head + cases.tail.foreach(c => checkSameUniverse(head, c, "be combined in branches of a match", c.pos)) + } tree.withType(ctx.typeComparer.lub(cases.tpes)) + } def assignType(tree: untpd.Return)(implicit ctx: Context) = tree.withType(defn.NothingType) @@ -449,11 +458,15 @@ trait TypeAssigner { def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = tree.withType(ref.tpe) - def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = { + checkSameUniverse(left, right, "be combined in `&`", tree.pos) tree.withType(left.tpe & right.tpe) + } - def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = { + checkSameUniverse(left, right, "be combined in `|`", tree.pos) tree.withType(left.tpe | right.tpe) + } /** Assign type of RefinedType. * Refinements are typed as if they were members of refinement class `refineCls`. @@ -483,8 +496,10 @@ trait TypeAssigner { def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = tree.withType(ExprType(result.tpe)) - def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = + def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = { + checkSameUniverse(lo, hi, "be combined in type bounds.", tree.pos) tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe)) + } def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) = tree.withType(NamedType.withFixedSym(NoPrefix, sym)) @@ -529,6 +544,12 @@ trait TypeAssigner { def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = tree.withType(pid.symbol.valRef) + + private def checkSameUniverse(tree1: Tree, tree2: Tree, relationship: => String, pos: Position)(implicit ctx: Context) = { + if (tree1.tpe.topType != tree2.tpe.topType) + ctx.error(ex"${tree1.tpe} and ${tree2.tpe} are in different universes. They cannot $relationship", pos) + } + } object TypeAssigner extends TypeAssigner diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 565f34f95d0b..fcb9b1d6f887 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -651,12 +651,6 @@ 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.topType != elsep1.tpe.topType) { - ctx.error(s"""if/else cannot have branches with types in different lattices: - | ${thenp1.tpe.show} of lattice ${thenp1.tpe.topType.show} - | ${elsep1.tpe.show} of lattice ${elsep1.tpe.topType.show} - """.stripMargin, tree.pos) - } val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil) assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } @@ -828,8 +822,6 @@ 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("Cannot pattern match on phantoms", sel1.pos) val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen val cases1 = typedCases(tree.cases, selType, pt.notApplied) @@ -860,15 +852,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit accu(Set.empty, selType) } - val tpdCases = cases mapconserve (typedCase(_, pt, selType, gadtSyms)) - - if (tpdCases.nonEmpty) { - val top = tpdCases.head.tpe.topType - for (tpdCase <- tpdCases if tpdCase.tpe.topType != top) - ctx.error(s"Pattern expected case to return a ${top.show} but was ${tpdCase.tpe.show}", tpdCase.pos) - } - - tpdCases + cases mapconserve (typedCase(_, pt, selType, gadtSyms)) } /** Type a case. Overridden in ReTyper, that's why it's separate from @@ -1658,38 +1642,8 @@ 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? - - /** Check that there 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] = { - tree match { - case TypeBoundsTree(lo, hi) => - val allTops = checkedTops(hi) union checkedTops(lo) - if (allTops.size <= 1) allTops - else { - ctx.error("Type can not be bounded at the same time by types in different latices: " + - allTops.map(_.show).mkString(", "), tree.pos) - Set.empty - } - case untpd.InfixOp(left, op, right) => - val allTops = checkedTops(left) union checkedTops(right) - if (allTops.size <= 1) allTops - else { - ctx.error(s"Can not use ${op.show} mix types of different lattices: " + - allTops.map(_.show).mkString(", "), tree.pos) - Set.empty - } - case EmptyTree => Set.empty - case _ => Set(tree.typeOpt.topType) - } - } - checkedTops(tree) - + def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern? 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) From a50328f4c04bcd78c305c98bc90cb4dd1b2fad9d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 Apr 2017 10:02:15 +0200 Subject: [PATCH 16/32] Remove PhantomTypeErasure and integrate into Erasure. This removes an unnecessary additional phase and fixes siggnatures of erased methods. --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 - .../dotty/tools/dotc/core/Definitions.scala | 4 +- .../dotty/tools/dotc/core/TypeErasure.scala | 7 ++- .../dotty/tools/dotc/transform/Erasure.scala | 9 ++- .../phantom/PhantomTypeErasure.scala | 56 ------------------- .../dotty/tools/dotc/CompilationTests.scala | 1 + .../dotty/runtime/ErasedPhantomLattice.scala | 3 - tests/neg/customArgs/phantom-overload.scala | 28 ++++++++++ 8 files changed, 43 insertions(+), 67 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala delete mode 100644 library/src/dotty/runtime/ErasedPhantomLattice.scala create mode 100644 tests/neg/customArgs/phantom-overload.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 3cac60c32cb9..8ee016117de3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -11,7 +11,6 @@ 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 @@ -77,7 +76,6 @@ class Compiler { 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. - 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 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index adf9cf159327..9fac6e1a4a25 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -991,8 +991,6 @@ class Definitions { sym.name == nme.assume_ && (sym.owner eq PhantomClass) lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") - def ErasedPhantomType = ErasedPhantomClass.typeRef + def ErasedPhantomType(implicit ctx: Context) = ErasedPhantomClass.typeRef - lazy val ErasedPhantomLatticeClass = ctx.requiredClass("dotty.runtime.ErasedPhantomLattice") - def ErasedPhantomLatticeType = ErasedPhantomLatticeClass.typeRef } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index b8a7d2aeed97..e1e0bf3f624e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,6 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) + else if (defn.isPhantomAnyClass(tp.symbol) || defn.isPhantomNothingClass(tp.symbol)) defn.ErasedPhantomType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -402,8 +403,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else classParents.mapConserve(eraseTypeRef) match { case tr :: trs1 => assert(!tr.classSymbol.is(Trait), cls) - val tr1 = if (cls is Trait) defn.ObjectType else tr - tr1 :: trs1.filterNot(_ isRef defn.ObjectClass) + val tr1 = if (cls.is(Trait) || (tr.symbol eq defn.PhantomClass)) defn.ObjectType else tr + tr1 :: trs1.filterNot(x => x.isRef(defn.ObjectClass) || x.isRef(defn.PhantomClass)) case nil => nil } val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass) @@ -506,6 +507,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) + else if (defn.isPhantomAnyClass(tp.symbol) || defn.isPhantomNothingClass(tp.symbol)) + sigName(defn.ErasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayOf(elem) => diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 1d761f32acb0..8d181945fa20 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -434,7 +434,14 @@ object Erasure extends TypeTestsCasts{ override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) = { val ntree = interceptTypeApply(tree.asInstanceOf[TypeApply])(ctx.withPhase(ctx.erasurePhase)) - ntree match { + if (defn.isPhantomAssume(tree.fun.symbol)) { + /* All phantom types are erased to `ErasedPhantom` (an uninstantiable final abstract class), + * hence the only valid term for a `ErasedPhantom` is `null`. + * As `Phantom.assume[P <: Phantom.Any]` is the only way to instantiate phantoms, all runtime + * values of phantom type become `null` (no instantiation overhead). + */ + Literal(Constant(null)).withType(defn.ErasedPhantomType) + } else ntree match { case TypeApply(fun, args) => val fun1 = typedExpr(fun, WildcardType) fun1.tpe.widen match { diff --git a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala b/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala deleted file mode 100644 index 73d19c689f24..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/phantom/PhantomTypeErasure.scala +++ /dev/null @@ -1,56 +0,0 @@ -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.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" - - /** 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 => - val sym = tp.classSymbol - if (defn.isPhantomAnyClass(sym) || defn.isPhantomNothingClass(sym)) defn.ErasedPhantomType - else if (sym eq defn.PhantomClass) defn.ErasedPhantomLatticeType - else mapOver(tp) - 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.paramInfos, defn.ObjectType) - case _ => mapOver(tp) - } - } - erasePhantomAnyTypeMap(tp) - } - -} diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c46d709a87ff..b701e9e1be24 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -152,6 +152,7 @@ class CompilationTests extends ParallelTesting { compileFile("../tests/neg/customArgs/noimports2.scala", defaultOptions.and("-Yno-imports")) + compileFile("../tests/neg/customArgs/overloadsOnAbstractTypes.scala", allowDoubleBindings) + compileFile("../tests/neg/customArgs/xfatalWarnings.scala", defaultOptions.and("-Xfatal-warnings")) + + compileFile("../tests/neg/customArgs/phantom-overload.scala", allowDoubleBindings) + compileFile("../tests/neg/tailcall/t1672b.scala", defaultOptions) + compileFile("../tests/neg/tailcall/t3275.scala", defaultOptions) + compileFile("../tests/neg/tailcall/t6574.scala", defaultOptions) + diff --git a/library/src/dotty/runtime/ErasedPhantomLattice.scala b/library/src/dotty/runtime/ErasedPhantomLattice.scala deleted file mode 100644 index e00c378a7ad6..000000000000 --- a/library/src/dotty/runtime/ErasedPhantomLattice.scala +++ /dev/null @@ -1,3 +0,0 @@ -package dotty.runtime - -abstract class ErasedPhantomLattice diff --git a/tests/neg/customArgs/phantom-overload.scala b/tests/neg/customArgs/phantom-overload.scala new file mode 100644 index 000000000000..0ac290b0775f --- /dev/null +++ b/tests/neg/customArgs/phantom-overload.scala @@ -0,0 +1,28 @@ + +class phantomOverload { + import Boo._ + import Boo2._ + + def foo1(): A = nothing + def foo1(): B = nothing // error + def foo1(): C = nothing2 // error + def foo1(): N = nothing // error + + def foo2(x: A) = ??? + def foo2(x: A) = ??? // error + def foo2(x: B) = ??? // error + def foo2(x: C) = ??? // error + def foo2(x: N) = ??? // error +} + +object Boo extends Phantom { + type A <: this.Any + type B <: this.Any + type N = this.Nothing + def nothing = assume[this.Nothing] +} + +object Boo2 extends Phantom { + type C <: this.Any + def nothing2 = assume[this.Nothing] +} \ No newline at end of file From 9502cb27b280085617b8f2405c0f43651a19c943 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 11 Apr 2017 22:01:05 +0200 Subject: [PATCH 17/32] Slighly faster phantom erasure checks. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9fac6e1a4a25..1b048145113f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -981,8 +981,8 @@ class Definitions { cls } - def isPhantomAnyClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name == tpnme.Any && (sym.owner eq PhantomClass) + def isPhantomAnyOrNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = + (sym.name == tpnme.Any || sym.name == tpnme.Nothing) && (sym.owner eq PhantomClass) def isPhantomNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = sym.name == tpnme.Nothing && (sym.owner eq PhantomClass) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index e1e0bf3f624e..f8b49bd7ce11 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if (defn.isPhantomAnyClass(tp.symbol) || defn.isPhantomNothingClass(tp.symbol)) defn.ErasedPhantomType + else if (defn.isPhantomAnyOrNothingClass(tp.symbol)) defn.ErasedPhantomType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -507,7 +507,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) - else if (defn.isPhantomAnyClass(tp.symbol) || defn.isPhantomNothingClass(tp.symbol)) + else if (defn.isPhantomAnyOrNothingClass(tp.symbol)) sigName(defn.ErasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName From 6f03972194af013f33a7c1d1caf70824099e0800 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 17 Apr 2017 18:19:53 +0200 Subject: [PATCH 18/32] Simplify the definition of Phantom.assume. --- .../dotty/tools/dotc/core/Definitions.scala | 5 +---- .../dotty/tools/dotc/transform/Erasure.scala | 18 +++++++++--------- library/src/scala/Phantom.scala | 4 ++-- tests/neg/customArgs/phantom-overload.scala | 4 ++-- tests/neg/phantom-Eq.scala | 13 ++++++------- tests/neg/phantom-bottom.scala | 4 ++-- tests/neg/phantom-evidence.scala | 2 +- tests/neg/phantom-expr.scala | 4 ++-- tests/neg/phantom-fun-app.scala | 2 +- tests/neg/phantom-instanceOf-1.scala | 2 +- tests/neg/phantom-instanceOf-2.scala | 2 +- tests/neg/phantom-trait-1.scala | 2 +- tests/neg/phantom-type-param-bounds-1.scala | 2 +- tests/neg/phantom-type-param-bounds-2.scala | 2 +- tests/pos/phantom-Eq.scala | 12 ++++++------ tests/pos/phantom-Eq2/Phantom-Eq_1.scala | 12 ++++++------ tests/run/phantom-1.scala | 2 +- tests/run/phantom-2.scala | 2 +- tests/run/phantom-3.scala | 2 +- tests/run/phantom-4.scala | 2 +- tests/run/phantom-5.scala | 2 +- tests/run/phantom-decls-1.scala | 2 +- tests/run/phantom-decls-2.scala | 2 +- tests/run/phantom-decls-3.scala | 2 +- tests/run/phantom-decls-4.scala | 2 +- tests/run/phantom-decls-5.scala | 2 +- tests/run/phantom-hk-1.scala | 2 +- tests/run/phantom-hk-2.scala | 2 +- tests/run/phantom-lazy-val.scala | 2 +- tests/run/phantom-methods-1.scala | 2 +- tests/run/phantom-methods-10.scala | 2 +- tests/run/phantom-methods-2.scala | 2 +- tests/run/phantom-methods-3.scala | 2 +- tests/run/phantom-methods-4.scala | 2 +- tests/run/phantom-methods-5.scala | 2 +- tests/run/phantom-methods-6.scala | 2 +- tests/run/phantom-methods-7.scala | 2 +- tests/run/phantom-methods-8.scala | 2 +- tests/run/phantom-methods-9.scala | 2 +- tests/run/phantom-poly-1.scala | 2 +- tests/run/phantom-poly-2.scala | 2 +- tests/run/phantom-poly-3.scala | 2 +- tests/run/phantom-poly-4.scala | 2 +- tests/run/phantom-val.scala | 2 +- tests/run/phantom-var.scala | 2 +- 45 files changed, 72 insertions(+), 76 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 1b048145113f..058ff264c17a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -973,10 +973,7 @@ class Definitions { val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil) val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef)) - - val tparamNames = List("P".toTypeName) - val ptype = PolyType(tparamNames)(_ => TypeBounds(nothing.typeRef, any.typeRef) :: Nil, TypeParamRef(_, 0)) - newSymbol(cls, nme.assume_, Protected | Final | Method, ptype).entered + enterMethod(cls, nme.assume_, MethodType(Nil, nothing.typeRef), Protected | Final | Method) cls } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 8d181945fa20..79ae2f8cbba0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -434,14 +434,7 @@ object Erasure extends TypeTestsCasts{ override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) = { val ntree = interceptTypeApply(tree.asInstanceOf[TypeApply])(ctx.withPhase(ctx.erasurePhase)) - if (defn.isPhantomAssume(tree.fun.symbol)) { - /* All phantom types are erased to `ErasedPhantom` (an uninstantiable final abstract class), - * hence the only valid term for a `ErasedPhantom` is `null`. - * As `Phantom.assume[P <: Phantom.Any]` is the only way to instantiate phantoms, all runtime - * values of phantom type become `null` (no instantiation overhead). - */ - Literal(Constant(null)).withType(defn.ErasedPhantomType) - } else ntree match { + ntree match { case TypeApply(fun, args) => val fun1 = typedExpr(fun, WildcardType) fun1.tpe.widen match { @@ -461,7 +454,14 @@ object Erasure extends TypeTestsCasts{ val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else typedExpr(fun, FunProto(args, pt, this)) match { + else if (defn.isPhantomAssume(fun.symbol)) { + /* All phantom types are erased to `ErasedPhantom` (an un-instantiable final abstract class), + * hence the only valid term for a `ErasedPhantom` is `null`. + * As `Phantom.assume` is the only way to instantiate phantoms, all runtime values of + * phantom type become `null` (no instantiation overhead). + */ + Literal(Constant(null)).withType(defn.ErasedPhantomType) + } else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 case fun1 => diff --git a/library/src/scala/Phantom.scala b/library/src/scala/Phantom.scala index f8f52f22c93f..0fbd70cc96bc 100644 --- a/library/src/scala/Phantom.scala +++ b/library/src/scala/Phantom.scala @@ -7,7 +7,7 @@ trait Phantom { 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 + protected final def assume: this.Nothing = + null.asInstanceOf[this.Nothing] // This implementation matches the erased implementation } */ diff --git a/tests/neg/customArgs/phantom-overload.scala b/tests/neg/customArgs/phantom-overload.scala index 0ac290b0775f..d4de97c7bbd3 100644 --- a/tests/neg/customArgs/phantom-overload.scala +++ b/tests/neg/customArgs/phantom-overload.scala @@ -19,10 +19,10 @@ object Boo extends Phantom { type A <: this.Any type B <: this.Any type N = this.Nothing - def nothing = assume[this.Nothing] + def nothing: this.Nothing = assume } object Boo2 extends Phantom { type C <: this.Any - def nothing2 = assume[this.Nothing] + def nothing2: this.Nothing = assume } \ No newline at end of file diff --git a/tests/neg/phantom-Eq.scala b/tests/neg/phantom-Eq.scala index db6d2744cb3e..6d4fcb653260 100644 --- a/tests/neg/phantom-Eq.scala +++ b/tests/neg/phantom-Eq.scala @@ -26,14 +26,13 @@ object EqUtil extends Phantom { 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 eqString: PhantomEqEq[String] = assume + implicit def eqInt: PhantomEqEq[Int] = assume + implicit def eqDouble: PhantomEqEq[Double] = assume - implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] - implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + implicit def eqByteNum: PhantomEq[Byte, Number] = assume + implicit def eqNumByte: PhantomEq[Number, Byte] = assume - implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = - assume[PhantomEq[Seq[T], Seq[U]]] + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume } diff --git a/tests/neg/phantom-bottom.scala b/tests/neg/phantom-bottom.scala index 6d54c7342153..7f6f2c2ff333 100644 --- a/tests/neg/phantom-bottom.scala +++ b/tests/neg/phantom-bottom.scala @@ -16,10 +16,10 @@ class Foo { object Boo extends Phantom { type BooAny = this.Any type BooNothing = this.Nothing - def nothing: BooNothing = assume[BooNothing] + def nothing: BooNothing = assume } object Boo2 extends Phantom { type BooNothing2 = this.Nothing - def nothing: BooNothing2 = assume[BooNothing2] + def nothing: BooNothing2 = assume } \ No newline at end of file diff --git a/tests/neg/phantom-evidence.scala b/tests/neg/phantom-evidence.scala index b9e9d288867b..1ba0477bc2d4 100644 --- a/tests/neg/phantom-evidence.scala +++ b/tests/neg/phantom-evidence.scala @@ -13,7 +13,7 @@ object WithNormalState extends Phantom { type =::=[From, To] <: this.Any - implicit inline def tpEquals[A]: A =::= A = assume[=::=[A, A]] + implicit inline def tpEquals[A]: A =::= A = assume trait State sealed trait On extends State diff --git a/tests/neg/phantom-expr.scala b/tests/neg/phantom-expr.scala index 7801c05c0a90..1ebe92e30792 100644 --- a/tests/neg/phantom-expr.scala +++ b/tests/neg/phantom-expr.scala @@ -36,10 +36,10 @@ class Foo { object Boo extends Phantom { type BooAny = this.Any - def boo[B <: BooAny]: B = assume[B] + def boo[B <: BooAny]: B = assume } object Boo1 extends Phantom { type Boo1Any = this.Any - def boo1: Boo1Any = assume[Boo1Any] + def boo1: Boo1Any = assume } \ No newline at end of file diff --git a/tests/neg/phantom-fun-app.scala b/tests/neg/phantom-fun-app.scala index bcd3a35c079a..4e6e4424acec 100644 --- a/tests/neg/phantom-fun-app.scala +++ b/tests/neg/phantom-fun-app.scala @@ -20,5 +20,5 @@ object Boo extends Phantom { type BooAny = this.Any type Blinky <: BooAny type Pinky <: Blinky - def boo[B <: BooAny]: B = assume[B] + def boo[B <: BooAny]: B = assume } diff --git a/tests/neg/phantom-instanceOf-1.scala b/tests/neg/phantom-instanceOf-1.scala index d7117d6e9562..7135c4281563 100644 --- a/tests/neg/phantom-instanceOf-1.scala +++ b/tests/neg/phantom-instanceOf-1.scala @@ -7,5 +7,5 @@ class phantomInstanceOf1 { } object Boo extends Phantom { - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/neg/phantom-instanceOf-2.scala b/tests/neg/phantom-instanceOf-2.scala index 9a6fdc3ed4e5..c4164618623f 100644 --- a/tests/neg/phantom-instanceOf-2.scala +++ b/tests/neg/phantom-instanceOf-2.scala @@ -10,5 +10,5 @@ class phantomInstanceOf2 { object Boo extends Phantom { type BooAny <: this.Any type Blinky <: this.Any - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/neg/phantom-trait-1.scala b/tests/neg/phantom-trait-1.scala index 2b4ca6a7b053..bdf2429d56eb 100644 --- a/tests/neg/phantom-trait-1.scala +++ b/tests/neg/phantom-trait-1.scala @@ -1,4 +1,4 @@ object Boo extends Phantom { - override def assume[P >: this.Nothing <: this.Any]: P = super.assume[P] // error + override val assume: this.Nothing = super.assume // error } diff --git a/tests/neg/phantom-type-param-bounds-1.scala b/tests/neg/phantom-type-param-bounds-1.scala index 5a1921c51d6b..5e2c7fe60579 100644 --- a/tests/neg/phantom-type-param-bounds-1.scala +++ b/tests/neg/phantom-type-param-bounds-1.scala @@ -5,5 +5,5 @@ class phantomTypeParamBounds1 { } object Boo extends Phantom { - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/neg/phantom-type-param-bounds-2.scala b/tests/neg/phantom-type-param-bounds-2.scala index 9336ea821882..4f6d4ec06051 100644 --- a/tests/neg/phantom-type-param-bounds-2.scala +++ b/tests/neg/phantom-type-param-bounds-2.scala @@ -9,5 +9,5 @@ class phantomTypeParamBounds2 { } object Boo extends Phantom { - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/pos/phantom-Eq.scala b/tests/pos/phantom-Eq.scala index bc216b6323a0..dcb8e213b912 100644 --- a/tests/pos/phantom-Eq.scala +++ b/tests/pos/phantom-Eq.scala @@ -31,12 +31,12 @@ object EqUtil extends Phantom { 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 eqString: PhantomEqEq[String] = assume + implicit def eqInt: PhantomEqEq[Int] = assume + implicit def eqDouble: PhantomEqEq[Double] = assume - implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] - implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + implicit def eqByteNum: PhantomEq[Byte, Number] = assume + implicit def eqNumByte: PhantomEq[Number, Byte] = assume - implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume[PhantomEq[Seq[T], Seq[U]]] + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume } diff --git a/tests/pos/phantom-Eq2/Phantom-Eq_1.scala b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala index 70a5a747409e..3d03c64e63a1 100644 --- a/tests/pos/phantom-Eq2/Phantom-Eq_1.scala +++ b/tests/pos/phantom-Eq2/Phantom-Eq_1.scala @@ -9,12 +9,12 @@ object EqUtil extends Phantom { 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 eqString: PhantomEqEq[String] = assume + implicit def eqInt: PhantomEqEq[Int] = assume + implicit def eqDouble: PhantomEqEq[Double] = assume - implicit def eqByteNum: PhantomEq[Byte, Number] = assume[PhantomEq[Byte, Number]] - implicit def eqNumByte: PhantomEq[Number, Byte] = assume[PhantomEq[Number, Byte]] + implicit def eqByteNum: PhantomEq[Byte, Number] = assume + implicit def eqNumByte: PhantomEq[Number, Byte] = assume - implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume[PhantomEq[Seq[T], Seq[U]]] + implicit def eqSeq[T, U](implicit eq: PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = assume } diff --git a/tests/run/phantom-1.scala b/tests/run/phantom-1.scala index 942de9dedb1f..38cdfaaf2236 100644 --- a/tests/run/phantom-1.scala +++ b/tests/run/phantom-1.scala @@ -18,5 +18,5 @@ object Test { object Boo extends Phantom { type BooAny = this.Any - def any: BooAny = assume[BooAny] + def any: BooAny = assume } diff --git a/tests/run/phantom-2.scala b/tests/run/phantom-2.scala index d1f4e7a55150..7d954dfa6b36 100644 --- a/tests/run/phantom-2.scala +++ b/tests/run/phantom-2.scala @@ -16,5 +16,5 @@ object Test { object Boo extends Phantom { type BooNothing = this.Nothing - def nothig: BooNothing = assume[BooNothing] // Should be allowed? + def nothig: BooNothing = assume } diff --git a/tests/run/phantom-3.scala b/tests/run/phantom-3.scala index 3c8d1dc35152..45be3122f3b3 100644 --- a/tests/run/phantom-3.scala +++ b/tests/run/phantom-3.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Inky <: Blinky type Pinky <: Inky type Casper = Pinky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-4.scala b/tests/run/phantom-4.scala index 86c6b4e2bac1..1c1d58262068 100644 --- a/tests/run/phantom-4.scala +++ b/tests/run/phantom-4.scala @@ -23,5 +23,5 @@ object Boo extends Phantom { type Inky <: Blinky type Pinky <: Inky type Casper = Pinky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-5.scala b/tests/run/phantom-5.scala index ccb0c191ded9..8be113d8e2c0 100644 --- a/tests/run/phantom-5.scala +++ b/tests/run/phantom-5.scala @@ -23,5 +23,5 @@ object Boo extends Phantom { type Pinky <: Inky type Clyde >: Pinky <: Inky type Casper = Pinky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-decls-1.scala b/tests/run/phantom-decls-1.scala index f73c88f3c4bb..c2781abf1696 100644 --- a/tests/run/phantom-decls-1.scala +++ b/tests/run/phantom-decls-1.scala @@ -25,5 +25,5 @@ object Boo extends Phantom { type BooAny = this.Any type Blinky <: this.Any type Inky <: Blinky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-decls-2.scala b/tests/run/phantom-decls-2.scala index 176c28bb6d1a..4a4f594fe5f3 100644 --- a/tests/run/phantom-decls-2.scala +++ b/tests/run/phantom-decls-2.scala @@ -27,5 +27,5 @@ object Boo extends Phantom { type Inky <: Blinky type Pinky <: Inky type Casper = Pinky - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/run/phantom-decls-3.scala b/tests/run/phantom-decls-3.scala index e7137ae8a412..fe18c8651f04 100644 --- a/tests/run/phantom-decls-3.scala +++ b/tests/run/phantom-decls-3.scala @@ -32,5 +32,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: BooAny]: B = assume[B] + def boo[B <: BooAny]: B = assume } diff --git a/tests/run/phantom-decls-4.scala b/tests/run/phantom-decls-4.scala index abc0e8fb5ff3..fe9d9251a329 100644 --- a/tests/run/phantom-decls-4.scala +++ b/tests/run/phantom-decls-4.scala @@ -21,5 +21,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-decls-5.scala b/tests/run/phantom-decls-5.scala index edb404ef1ab0..f5bb1a8ef711 100644 --- a/tests/run/phantom-decls-5.scala +++ b/tests/run/phantom-decls-5.scala @@ -20,5 +20,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-hk-1.scala b/tests/run/phantom-hk-1.scala index 0f4a8b48baa3..0e6692f5b730 100644 --- a/tests/run/phantom-hk-1.scala +++ b/tests/run/phantom-hk-1.scala @@ -27,5 +27,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/run/phantom-hk-2.scala b/tests/run/phantom-hk-2.scala index d75cf13c5289..01fdaa893c62 100644 --- a/tests/run/phantom-hk-2.scala +++ b/tests/run/phantom-hk-2.scala @@ -25,5 +25,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-lazy-val.scala b/tests/run/phantom-lazy-val.scala index b6214b9b40cf..4764d1d5cbe7 100644 --- a/tests/run/phantom-lazy-val.scala +++ b/tests/run/phantom-lazy-val.scala @@ -23,5 +23,5 @@ object Test { object Boo extends Phantom { type BooAny = this.Any - def any: BooAny = assume[BooAny] + def any: BooAny = assume } diff --git a/tests/run/phantom-methods-1.scala b/tests/run/phantom-methods-1.scala index c55dce758219..e823675cff07 100644 --- a/tests/run/phantom-methods-1.scala +++ b/tests/run/phantom-methods-1.scala @@ -18,5 +18,5 @@ object Test { object Boo extends Phantom { type BooAny = Boo.Any type Pinky <: Boo.Any - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-10.scala b/tests/run/phantom-methods-10.scala index a9bc3a11b01c..de2ea0685ffa 100644 --- a/tests/run/phantom-methods-10.scala +++ b/tests/run/phantom-methods-10.scala @@ -27,5 +27,5 @@ object Test { object Boo extends Phantom { type Inky <: this.Any - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/run/phantom-methods-2.scala b/tests/run/phantom-methods-2.scala index 17e7f1982a06..e85784f31ca7 100644 --- a/tests/run/phantom-methods-2.scala +++ b/tests/run/phantom-methods-2.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-3.scala b/tests/run/phantom-methods-3.scala index e74b03eb6022..769c7c88f22e 100644 --- a/tests/run/phantom-methods-3.scala +++ b/tests/run/phantom-methods-3.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-4.scala b/tests/run/phantom-methods-4.scala index 0843c106d512..070629037d54 100644 --- a/tests/run/phantom-methods-4.scala +++ b/tests/run/phantom-methods-4.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-5.scala b/tests/run/phantom-methods-5.scala index 354f1aa4f50a..5a59c9ef7e94 100644 --- a/tests/run/phantom-methods-5.scala +++ b/tests/run/phantom-methods-5.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-6.scala b/tests/run/phantom-methods-6.scala index c609d23cc7d2..a994abc5b8f1 100644 --- a/tests/run/phantom-methods-6.scala +++ b/tests/run/phantom-methods-6.scala @@ -20,5 +20,5 @@ object Boo extends Phantom { type Blinky <: Boo.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-7.scala b/tests/run/phantom-methods-7.scala index b7128aad2ce1..afdc7b8c7aa2 100644 --- a/tests/run/phantom-methods-7.scala +++ b/tests/run/phantom-methods-7.scala @@ -20,5 +20,5 @@ object Boo extends Phantom { type Inky <: Boo.Any type Pinky <: Inky type Clyde >: Pinky <: Inky - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-8.scala b/tests/run/phantom-methods-8.scala index 6c3d89942a44..c0bf096538aa 100644 --- a/tests/run/phantom-methods-8.scala +++ b/tests/run/phantom-methods-8.scala @@ -22,5 +22,5 @@ object Test { object Boo extends Phantom { type Inky <: Boo.Any - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-methods-9.scala b/tests/run/phantom-methods-9.scala index 1a6cce79d3ad..4feb7bfcc730 100644 --- a/tests/run/phantom-methods-9.scala +++ b/tests/run/phantom-methods-9.scala @@ -27,5 +27,5 @@ object Test { object Boo extends Phantom { type Inky <: Boo.Any - def boo[B <: Boo.Any]: B = assume[B] + def boo[B <: Boo.Any]: B = assume } diff --git a/tests/run/phantom-poly-1.scala b/tests/run/phantom-poly-1.scala index 6c6f72d86580..4f16c5dcb325 100644 --- a/tests/run/phantom-poly-1.scala +++ b/tests/run/phantom-poly-1.scala @@ -19,5 +19,5 @@ object Test { object Boo extends Phantom { type Casper <: this.Any - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/run/phantom-poly-2.scala b/tests/run/phantom-poly-2.scala index e96e17f913cb..02152bf0ea4d 100644 --- a/tests/run/phantom-poly-2.scala +++ b/tests/run/phantom-poly-2.scala @@ -21,5 +21,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: this.Any]: B = assume[B] + def boo[B <: this.Any]: B = assume } diff --git a/tests/run/phantom-poly-3.scala b/tests/run/phantom-poly-3.scala index a21b1c544827..92fdced4d855 100644 --- a/tests/run/phantom-poly-3.scala +++ b/tests/run/phantom-poly-3.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-poly-4.scala b/tests/run/phantom-poly-4.scala index 4502da5619eb..4bd2a3127808 100644 --- a/tests/run/phantom-poly-4.scala +++ b/tests/run/phantom-poly-4.scala @@ -22,5 +22,5 @@ object Boo extends Phantom { type Blinky <: this.Any type Inky <: Blinky type Pinky <: Inky - def boo[B <: Blinky]: B = assume[B] + def boo[B <: Blinky]: B = assume } diff --git a/tests/run/phantom-val.scala b/tests/run/phantom-val.scala index 455c188026b4..d6d42e008d96 100644 --- a/tests/run/phantom-val.scala +++ b/tests/run/phantom-val.scala @@ -22,5 +22,5 @@ object Test { object Boo extends Phantom { type BooAny = this.Any - def any: BooAny = assume[BooAny] + def any: BooAny = assume } diff --git a/tests/run/phantom-var.scala b/tests/run/phantom-var.scala index 1f0aae4a686b..dda20fe92bfe 100644 --- a/tests/run/phantom-var.scala +++ b/tests/run/phantom-var.scala @@ -26,5 +26,5 @@ object Test { object Boo extends Phantom { type BooAny = this.Any - def any: BooAny = assume[BooAny] + def any: BooAny = assume } From 621610fc3cb1101288db1561c6142226781d94e5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 20 Apr 2017 13:35:22 +0200 Subject: [PATCH 19/32] Cache PhantomNothing, PhantomAny and PhantomAssume symbols. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 12 +++--------- .../src/dotty/tools/dotc/core/TypeComparer.scala | 7 ++++++- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 4 ++-- .../src/dotty/tools/dotc/transform/Erasure.scala | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 058ff264c17a..2339c1430a7f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -977,15 +977,9 @@ class Definitions { cls } - - def isPhantomAnyOrNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = - (sym.name == tpnme.Any || sym.name == tpnme.Nothing) && (sym.owner eq PhantomClass) - - def isPhantomNothingClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name == tpnme.Nothing && (sym.owner eq PhantomClass) - - def isPhantomAssume(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name == nme.assume_ && (sym.owner eq PhantomClass) + lazy val PhantomAny = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass + lazy val PhantomNothing = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass + lazy val PhantomAssume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_) lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") def ErasedPhantomType(implicit ctx: Context) = ErasedPhantomClass.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index da37332c0cb3..e1e7eae22007 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -48,6 +48,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private var myAnyClass: ClassSymbol = null private var myNothingClass: ClassSymbol = null private var myNullClass: ClassSymbol = null + private var myPhantomNothingClass: ClassSymbol = null private var myObjectClass: ClassSymbol = null private var myAnyType: TypeRef = null private var myNothingType: TypeRef = null @@ -64,6 +65,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { if (myNullClass == null) myNullClass = defn.NullClass myNullClass } + def PhantomNothing = { + if (myPhantomNothingClass == null) myPhantomNothingClass = defn.PhantomNothing + myPhantomNothingClass + } def ObjectClass = { if (myObjectClass == null) myObjectClass = defn.ObjectClass myObjectClass @@ -553,7 +558,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } if (tp1.symbol eq NothingClass) tp2.isValueTypeOrLambda && (tp2.topType.classSymbol eq AnyClass) else if (tp1.symbol eq NullClass) isNullable(tp2) && (tp2.topType.classSymbol eq AnyClass) - else if (defn.isPhantomNothingClass(tp1.symbol)) tp2.isValueTypeOrLambda && (tp1.topType == tp2.topType) + else if (tp1.symbol eq PhantomNothing) tp2.isValueTypeOrLambda && (tp1.topType == tp2.topType) else false } case tp1: SingletonType => diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index f8b49bd7ce11..47ed1ef6a354 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if (defn.isPhantomAnyOrNothingClass(tp.symbol)) defn.ErasedPhantomType + else if ((tp.symbol eq defn.PhantomAny) || (tp.symbol eq defn.PhantomNothing)) defn.ErasedPhantomType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -507,7 +507,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) - else if (defn.isPhantomAnyOrNothingClass(tp.symbol)) + else if ((tp.symbol eq defn.PhantomAny) || (tp.symbol eq defn.PhantomNothing)) sigName(defn.ErasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 79ae2f8cbba0..506a11345cfc 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -454,7 +454,7 @@ object Erasure extends TypeTestsCasts{ val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else if (defn.isPhantomAssume(fun.symbol)) { + else if (fun.symbol eq defn.PhantomAssume) { /* All phantom types are erased to `ErasedPhantom` (an un-instantiable final abstract class), * hence the only valid term for a `ErasedPhantom` is `null`. * As `Phantom.assume` is the only way to instantiate phantoms, all runtime values of From ef62884e72418fb429ca85452ff93a8e47627b05 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 20 Apr 2017 14:43:28 +0200 Subject: [PATCH 20/32] Shortcircuit slow path typing Nothing <:< XYZ for non phantoms. --- .../tools/dotc/core/SymDenotations.scala | 6 ++++-- .../dotty/tools/dotc/core/TypeComparer.scala | 21 ++++++++++++++----- .../src/dotty/tools/dotc/core/Types.scala | 9 ++++++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b3a386914bf9..79d95c459689 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -634,8 +634,10 @@ object SymDenotations { } /** Is this symbol a class references to which that are supertypes of null? */ - final def isNullableClass(implicit ctx: Context): Boolean = - isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass + final def isNullableClass(implicit ctx: Context): Boolean = { + isClass && !isValueClass && !(this is ModuleClass) && + symbol != defn.NothingClass && symbol != defn.PhantomAny && symbol != defn.PhantomNothing + } /** Is this definition accessible as a member of tree with type `pre`? * @param pre The type of the tree from which the selection is made diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index e1e7eae22007..c5c35fa3dd32 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -48,6 +48,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private var myAnyClass: ClassSymbol = null private var myNothingClass: ClassSymbol = null private var myNullClass: ClassSymbol = null + private var myPhantomAnyClass: ClassSymbol = null private var myPhantomNothingClass: ClassSymbol = null private var myObjectClass: ClassSymbol = null private var myAnyType: TypeRef = null @@ -65,7 +66,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { if (myNullClass == null) myNullClass = defn.NullClass myNullClass } - def PhantomNothing = { + def PhantomAnyClass = { + if (myPhantomAnyClass == null) myPhantomAnyClass = defn.PhantomAny + myPhantomAnyClass + } + def PhantomNothingClass = { if (myPhantomNothingClass == null) myPhantomNothingClass = defn.PhantomNothing myPhantomNothingClass } @@ -556,10 +561,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false } - if (tp1.symbol eq NothingClass) tp2.isValueTypeOrLambda && (tp2.topType.classSymbol eq AnyClass) - else if (tp1.symbol eq NullClass) isNullable(tp2) && (tp2.topType.classSymbol eq AnyClass) - else if (tp1.symbol eq PhantomNothing) tp2.isValueTypeOrLambda && (tp1.topType == tp2.topType) - else false + def isPhantom(tp: Type): Boolean = { + // note that the only phantom classes are PhantomAnyClass and PhantomNothingClass + val sym = tp.typeSymbol + (sym eq PhantomAnyClass) || (sym eq PhantomNothingClass) || + (!sym.isClass && (tp.topType.classSymbol eq PhantomAnyClass)) + } + val sym1 = tp1.symbol + (sym1 eq NothingClass) && tp2.isValueTypeOrLambda && !isPhantom(tp2) || + (sym1 eq NullClass) && isNullable(tp2) || + (sym1 eq PhantomNothingClass) && tp1.topType == tp2.topType } 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/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 534b7ba1308f..b762976bfa47 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -176,7 +176,12 @@ object Types { } /** Is phantom if upper bounded by XYZ.Any where XYZ extends scala.Phantom */ - final def isPhantom(implicit ctx: Context): Boolean = isPhantomClass(topType.classSymbol) + final def isPhantom(implicit ctx: Context): Boolean = { + // note that the only phantom classes are PhantomAnyClass and PhantomNothingClass + val sym = typeSymbol + (sym eq defn.PhantomAny) || (sym eq defn.PhantomNothing) || + (!sym.isClass && (topType.classSymbol eq defn.PhantomAny)) + } /** Returns the top type of the lattice * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any @@ -201,7 +206,7 @@ object Types { /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.isClass && (sym.owner eq defn.PhantomClass) + (sym eq defn.PhantomAny) || (sym eq defn.PhantomNothing) /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could From 49e88fe5c989dfee52d6c0c007ae89784c02bd12 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2017 10:02:48 +0200 Subject: [PATCH 21/32] Faster bottomType and phantomness checks. --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 40 ++++++++++--------- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c5c35fa3dd32..5fbbe7164703 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -565,7 +565,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // note that the only phantom classes are PhantomAnyClass and PhantomNothingClass val sym = tp.typeSymbol (sym eq PhantomAnyClass) || (sym eq PhantomNothingClass) || - (!sym.isClass && (tp.topType.classSymbol eq PhantomAnyClass)) + (!sym.isClass && tp.phantomLatticeType.exists) } val sym1 = tp1.symbol (sym1 eq NothingClass) && tp2.isValueTypeOrLambda && !isPhantom(tp2) || diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b762976bfa47..b4dc0c22fc6f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -175,33 +175,35 @@ object Types { loop(this) } - /** Is phantom if upper bounded by XYZ.Any where XYZ extends scala.Phantom */ - final def isPhantom(implicit ctx: Context): Boolean = { - // note that the only phantom classes are PhantomAnyClass and PhantomNothingClass - val sym = typeSymbol - (sym eq defn.PhantomAny) || (sym eq defn.PhantomNothing) || - (!sym.isClass && (topType.classSymbol eq defn.PhantomAny)) - } - /** Returns the top type of the lattice * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - scala.Any otherwise */ - final def topType(implicit ctx: Context): TypeRef = widen match { - case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp.prefix.select(tpnme.Any).asInstanceOf[TypeRef] - case tp: TypeProxy if tp.superType ne this => tp.superType.topType - case tp: AndOrType => tp.tp1.topType - case _ => defn.AnyType + final def topType(implicit ctx: Context): TypeRef = { + val lattice = phantomLatticeType + if (lattice.exists) lattice.select(tpnme.Any).asInstanceOf[TypeRef] + else defn.AnyType } /** Returns the bottom type of the lattice * - XYZ.Nothing if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - scala.Nothing otherwise */ - final def bottomType(implicit ctx: Context): Type = topType match { - case top: TypeRef if top.prefix.termSymbol ne defn.ScalaPackageVal => - top.prefix.select(tpnme.Nothing) - case _ => defn.NothingType + final def bottomType(implicit ctx: Context): Type = { + val lattice = phantomLatticeType + if (lattice.exists) lattice.select(tpnme.Nothing).asInstanceOf[TypeRef] + else defn.NothingType + } + + /** Returns the type of the lattice + * - XYZ if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any + * - NoType otherwise + */ + final def phantomLatticeType(implicit ctx: Context): Type = widen match { + case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp.prefix + case tp: TypeProxy if tp.superType ne this => tp.superType.phantomLatticeType + case tp: AndOrType => tp.tp1.phantomLatticeType + case _ => NoType } /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ @@ -3308,8 +3310,8 @@ object Types { /** Type bounds >: lo <: hi */ abstract case class TypeBounds(lo: Type, hi: Type) extends CachedProxyType with TypeType { - assert(lo.isInstanceOf[TermType], lo) - assert(hi.isInstanceOf[TermType], hi) + assert(lo.isInstanceOf[TermType]) + assert(hi.isInstanceOf[TermType]) def variance: Int = 0 diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ec0fbeda7208..ff90f9a4700d 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -431,7 +431,7 @@ trait TypeAssigner { tree.withType(body.tpe) def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { - if (tree.selector.typeOpt.isPhantom) + if (tree.selector.typeOpt.phantomLatticeType.exists) ctx.error("Cannot pattern match on phantoms", tree.selector.pos) if (cases.nonEmpty) { val head = cases.head From e062df30793eb3fdd5dae3d96a302e35a007866d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 24 Apr 2017 09:54:08 +0200 Subject: [PATCH 22/32] Improve names of phantom classes in defn. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2339c1430a7f..db9456e423dd 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -977,8 +977,8 @@ class Definitions { cls } - lazy val PhantomAny = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass - lazy val PhantomNothing = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass + lazy val PhantomAnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass + lazy val PhantomNothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass lazy val PhantomAssume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_) lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 79d95c459689..3cb4e979b330 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -636,7 +636,7 @@ object SymDenotations { /** Is this symbol a class references to which that are supertypes of null? */ final def isNullableClass(implicit ctx: Context): Boolean = { isClass && !isValueClass && !(this is ModuleClass) && - symbol != defn.NothingClass && symbol != defn.PhantomAny && symbol != defn.PhantomNothing + symbol != defn.NothingClass && symbol != defn.PhantomAnyClass && symbol != defn.PhantomNothingClass } /** Is this definition accessible as a member of tree with type `pre`? diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 5fbbe7164703..d2547c35a742 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -67,11 +67,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { myNullClass } def PhantomAnyClass = { - if (myPhantomAnyClass == null) myPhantomAnyClass = defn.PhantomAny + if (myPhantomAnyClass == null) myPhantomAnyClass = defn.PhantomAnyClass myPhantomAnyClass } def PhantomNothingClass = { - if (myPhantomNothingClass == null) myPhantomNothingClass = defn.PhantomNothing + if (myPhantomNothingClass == null) myPhantomNothingClass = defn.PhantomNothingClass myPhantomNothingClass } def ObjectClass = { diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 47ed1ef6a354..6cd1d63cda54 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if ((tp.symbol eq defn.PhantomAny) || (tp.symbol eq defn.PhantomNothing)) defn.ErasedPhantomType + else if ((tp.symbol eq defn.PhantomAnyClass) || (tp.symbol eq defn.PhantomNothingClass)) defn.ErasedPhantomType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -507,7 +507,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) - else if ((tp.symbol eq defn.PhantomAny) || (tp.symbol eq defn.PhantomNothing)) + else if ((tp.symbol eq defn.PhantomAnyClass) || (tp.symbol eq defn.PhantomNothingClass)) sigName(defn.ErasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b4dc0c22fc6f..bdeaecbfa3e1 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -208,7 +208,7 @@ object Types { /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = - (sym eq defn.PhantomAny) || (sym eq defn.PhantomNothing) + (sym eq defn.PhantomAnyClass) || (sym eq defn.PhantomNothingClass) /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could From 3ff903eafb1b90144df873d9beb84f40d815c682 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 24 Apr 2017 10:26:17 +0200 Subject: [PATCH 23/32] Remove unnecessary changes. --- .../src/dotty/tools/dotc/core/Definitions.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index db9456e423dd..cd99ec44a041 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -149,18 +149,17 @@ class Definitions { } private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, - resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags, - bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = { + resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { val tparamNames = PolyType.syntheticParamNames(typeParamCount) - val tparamInfos = tparamNames map bounds + val tparamInfos = tparamNames map (_ => TypeBounds.empty) val ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn) enterMethod(cls, name, ptype, flags) } - private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet, bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = + private def enterT1ParameterlessMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = enterPolyMethod(cls, name, 1, resultTypeFn, flags) - private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet, bounds: TypeName => TypeBounds = _ => TypeBounds.empty) = + private def enterT1EmptyParamsMethod(cls: ClassSymbol, name: TermName, resultTypeFn: PolyType => Type, flags: FlagSet) = enterPolyMethod(cls, name, 1, pt => MethodType(Nil, resultTypeFn(pt)), flags) private def mkArityArray(name: String, arity: Int, countFrom: Int): Array[TypeRef] = { @@ -928,7 +927,7 @@ class Definitions { // ----- Initialization --------------------------------------------------- /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - private lazy val syntheticScalaClasses = List( + lazy val syntheticScalaClasses = List( AnyClass, AnyRefAlias, RepeatedParamClass, @@ -945,7 +944,7 @@ class Definitions { OpsPackageClass) /** Lists core methods that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ - private lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) + lazy val syntheticCoreMethods = AnyMethods ++ ObjectMethods ++ List(String_+, throwMethod) lazy val reservedScalaClassNames: Set[Name] = syntheticScalaClasses.map(_.name).toSet From f0383feb20d622540f3de5a41cb7afe70d8d499e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 26 Apr 2017 18:10:06 +0200 Subject: [PATCH 24/32] Remove ErasedPhantom class and use Unit. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++-- .../src/dotty/tools/dotc/core/SymDenotations.scala | 6 ++---- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/Types.scala | 6 +----- compiler/src/dotty/tools/dotc/transform/Erasure.scala | 11 +++-------- library/src/dotty/runtime/ErasedPhantom.scala | 3 --- library/src/scala/Phantom.scala | 3 +-- 7 files changed, 12 insertions(+), 27 deletions(-) delete mode 100644 library/src/dotty/runtime/ErasedPhantom.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index cd99ec44a041..cfcd7e33dfce 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -980,7 +980,7 @@ class Definitions { lazy val PhantomNothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass lazy val PhantomAssume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_) - lazy val ErasedPhantomClass = ctx.requiredClass("dotty.runtime.ErasedPhantom") - def ErasedPhantomType(implicit ctx: Context) = ErasedPhantomClass.typeRef + /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ + def isPhantomClass(sym: Symbol) = (sym eq PhantomAnyClass) || (sym eq PhantomNothingClass) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3cb4e979b330..0c6c0578dff0 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -634,10 +634,8 @@ object SymDenotations { } /** Is this symbol a class references to which that are supertypes of null? */ - final def isNullableClass(implicit ctx: Context): Boolean = { - isClass && !isValueClass && !(this is ModuleClass) && - symbol != defn.NothingClass && symbol != defn.PhantomAnyClass && symbol != defn.PhantomNothingClass - } + final def isNullableClass(implicit ctx: Context): Boolean = + isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass && !defn.isPhantomClass(symbol) /** Is this definition accessible as a member of tree with type `pre`? * @param pre The type of the tree from which the selection is made diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 6cd1d63cda54..e4e5aa88f0ef 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if ((tp.symbol eq defn.PhantomAnyClass) || (tp.symbol eq defn.PhantomNothingClass)) defn.ErasedPhantomType + else if (defn.isPhantomClass(tp.symbol)) defn.BoxedUnitType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -507,8 +507,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) - else if ((tp.symbol eq defn.PhantomAnyClass) || (tp.symbol eq defn.PhantomNothingClass)) - sigName(defn.ErasedPhantomType) + else if (defn.isPhantomClass(tp.symbol)) + sigName(defn.BoxedUnitType) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayOf(elem) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index bdeaecbfa3e1..0f68b0ed7a97 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -200,16 +200,12 @@ object Types { * - NoType otherwise */ final def phantomLatticeType(implicit ctx: Context): Type = widen match { - case tp: ClassInfo if isPhantomClass(tp.classSymbol) => tp.prefix + case tp: ClassInfo if defn.isPhantomClass(tp.classSymbol) => tp.prefix case tp: TypeProxy if tp.superType ne this => tp.superType.phantomLatticeType case tp: AndOrType => tp.tp1.phantomLatticeType case _ => NoType } - /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ - private def isPhantomClass(sym: Symbol)(implicit ctx: Context): Boolean = - (sym eq defn.PhantomAnyClass) || (sym eq defn.PhantomNothingClass) - /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could * be refined later. diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 506a11345cfc..23a2c6a696f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -454,14 +454,9 @@ object Erasure extends TypeTestsCasts{ val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else if (fun.symbol eq defn.PhantomAssume) { - /* All phantom types are erased to `ErasedPhantom` (an un-instantiable final abstract class), - * hence the only valid term for a `ErasedPhantom` is `null`. - * As `Phantom.assume` is the only way to instantiate phantoms, all runtime values of - * phantom type become `null` (no instantiation overhead). - */ - Literal(Constant(null)).withType(defn.ErasedPhantomType) - } else typedExpr(fun, FunProto(args, pt, this)) match { + else if (fun.symbol eq defn.PhantomAssume) + ref(defn.BoxedUnit_UNIT) + else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 case fun1 => diff --git a/library/src/dotty/runtime/ErasedPhantom.scala b/library/src/dotty/runtime/ErasedPhantom.scala deleted file mode 100644 index 9a0dc6e50c8f..000000000000 --- a/library/src/dotty/runtime/ErasedPhantom.scala +++ /dev/null @@ -1,3 +0,0 @@ -package dotty.runtime - -final abstract class ErasedPhantom diff --git a/library/src/scala/Phantom.scala b/library/src/scala/Phantom.scala index 0fbd70cc96bc..1d6d9792f111 100644 --- a/library/src/scala/Phantom.scala +++ b/library/src/scala/Phantom.scala @@ -7,7 +7,6 @@ trait Phantom { protected final abstract class Nothing extends Any - protected final def assume: this.Nothing = - null.asInstanceOf[this.Nothing] // This implementation matches the erased implementation + protected final def assume: this.Nothing } */ From 91558c3acf2eb35bf941fcbd21f2acfc7f41fee9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2017 13:43:05 +0200 Subject: [PATCH 25/32] Set flags of scala.Phantom to be NoInitsTrait. --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- library/src/scala/Phantom.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index cfcd7e33dfce..0d355f33461d 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -968,7 +968,7 @@ class Definitions { // ----- Phantoms --------------------------------------------------------- lazy val PhantomClass: ClassSymbol = { - val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, Abstract, List(AnyType))) + val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType))) val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil) val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef)) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index e4e5aa88f0ef..55036c7d3bf3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -403,7 +403,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else classParents.mapConserve(eraseTypeRef) match { case tr :: trs1 => assert(!tr.classSymbol.is(Trait), cls) - val tr1 = if (cls.is(Trait) || (tr.symbol eq defn.PhantomClass)) defn.ObjectType else tr + val tr1 = if (cls is Trait) defn.ObjectType else tr tr1 :: trs1.filterNot(x => x.isRef(defn.ObjectClass) || x.isRef(defn.PhantomClass)) case nil => nil } diff --git a/library/src/scala/Phantom.scala b/library/src/scala/Phantom.scala index 1d6d9792f111..4454a64cfe51 100644 --- a/library/src/scala/Phantom.scala +++ b/library/src/scala/Phantom.scala @@ -5,7 +5,7 @@ trait Phantom { /** Phantom.Any does not extend scala.Any */ protected /*final*/ trait Any - protected final abstract class Nothing extends Any + protected final trait Nothing extends this.Any protected final def assume: this.Nothing } From ec13270d967c60fb2c9a06996bb36078f6d290aa Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2017 09:45:53 +0200 Subject: [PATCH 26/32] Rename Phantoms in defn and add Type.isPhantom. --- .../src/dotty/tools/dotc/core/Definitions.scala | 8 ++++---- .../dotty/tools/dotc/core/SymDenotations.scala | 2 +- .../src/dotty/tools/dotc/core/TypeComparer.scala | 16 ++-------------- .../src/dotty/tools/dotc/core/TypeErasure.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- .../src/dotty/tools/dotc/transform/Erasure.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 8 files changed, 23 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 0d355f33461d..4bf2b36052de 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -976,11 +976,11 @@ class Definitions { cls } - lazy val PhantomAnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass - lazy val PhantomNothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass - lazy val PhantomAssume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_) + lazy val Phantom_AnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass + lazy val Phantom_NothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass + lazy val Phantom_assume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_) /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ - def isPhantomClass(sym: Symbol) = (sym eq PhantomAnyClass) || (sym eq PhantomNothingClass) + def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 0c6c0578dff0..310838d8aa73 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -635,7 +635,7 @@ object SymDenotations { /** Is this symbol a class references to which that are supertypes of null? */ final def isNullableClass(implicit ctx: Context): Boolean = - isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass && !defn.isPhantomClass(symbol) + isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass && !defn.isPhantomTerminalClass(symbol) /** Is this definition accessible as a member of tree with type `pre`? * @param pre The type of the tree from which the selection is made diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index d2547c35a742..0073257d96a4 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -12,7 +12,6 @@ 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. */ @@ -48,7 +47,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private var myAnyClass: ClassSymbol = null private var myNothingClass: ClassSymbol = null private var myNullClass: ClassSymbol = null - private var myPhantomAnyClass: ClassSymbol = null private var myPhantomNothingClass: ClassSymbol = null private var myObjectClass: ClassSymbol = null private var myAnyType: TypeRef = null @@ -66,12 +64,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { if (myNullClass == null) myNullClass = defn.NullClass myNullClass } - def PhantomAnyClass = { - if (myPhantomAnyClass == null) myPhantomAnyClass = defn.PhantomAnyClass - myPhantomAnyClass - } def PhantomNothingClass = { - if (myPhantomNothingClass == null) myPhantomNothingClass = defn.PhantomNothingClass + if (myPhantomNothingClass == null) myPhantomNothingClass = defn.Phantom_NothingClass myPhantomNothingClass } def ObjectClass = { @@ -561,14 +555,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false } - def isPhantom(tp: Type): Boolean = { - // note that the only phantom classes are PhantomAnyClass and PhantomNothingClass - val sym = tp.typeSymbol - (sym eq PhantomAnyClass) || (sym eq PhantomNothingClass) || - (!sym.isClass && tp.phantomLatticeType.exists) - } val sym1 = tp1.symbol - (sym1 eq NothingClass) && tp2.isValueTypeOrLambda && !isPhantom(tp2) || + (sym1 eq NothingClass) && tp2.isValueTypeOrLambda && !tp2.isPhantom || (sym1 eq NullClass) && isNullable(tp2) || (sym1 eq PhantomNothingClass) && tp1.topType == tp2.topType } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 55036c7d3bf3..5f8fcf243e40 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if (defn.isPhantomClass(tp.symbol)) defn.BoxedUnitType + else if (defn.isPhantomTerminalClass(tp.symbol)) defn.BoxedUnitType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -507,7 +507,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) - else if (defn.isPhantomClass(tp.symbol)) + else if (defn.isPhantomTerminalClass(tp.symbol)) sigName(defn.BoxedUnitType) else normalizeClass(sym.asClass).fullName.asTypeName diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0f68b0ed7a97..e2084cfdf9df 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -175,6 +175,12 @@ object Types { loop(this) } + /** Returns true if the type is a phantom type + * - true if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any + * - false otherwise + */ + final def isPhantom(implicit ctx: Context): Boolean = phantomLatticeType.exists + /** Returns the top type of the lattice * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - scala.Any otherwise @@ -195,13 +201,13 @@ object Types { else defn.NothingType } - /** Returns the type of the lattice + /** Returns the type of the lattice (i.e. the prefix of the phantom type) * - XYZ if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - NoType otherwise */ - final def phantomLatticeType(implicit ctx: Context): Type = widen match { - case tp: ClassInfo if defn.isPhantomClass(tp.classSymbol) => tp.prefix - case tp: TypeProxy if tp.superType ne this => tp.superType.phantomLatticeType + private final def phantomLatticeType(implicit ctx: Context): Type = widen match { + case tp: ClassInfo if defn.isPhantomTerminalClass(tp.classSymbol) => tp.prefix + case tp: TypeProxy if tp.underlying ne this => tp.underlying.phantomLatticeType case tp: AndOrType => tp.tp1.phantomLatticeType case _ => NoType } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 23a2c6a696f5..fe73b42eb735 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -454,7 +454,7 @@ object Erasure extends TypeTestsCasts{ val Apply(fun, args) = tree if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) - else if (fun.symbol eq defn.PhantomAssume) + else if (fun.symbol eq defn.Phantom_assume) ref(defn.BoxedUnit_UNIT) else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ff90f9a4700d..ec0fbeda7208 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -431,7 +431,7 @@ trait TypeAssigner { tree.withType(body.tpe) def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { - if (tree.selector.typeOpt.phantomLatticeType.exists) + if (tree.selector.typeOpt.isPhantom) ctx.error("Cannot pattern match on phantoms", tree.selector.pos) if (cases.nonEmpty) { val head = cases.head diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fcb9b1d6f887..6c618dffb70e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1112,8 +1112,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val lo1 = typed(lo) val hi1 = typed(hi) - val lo2 = if (!lo1.isEmpty) lo1 else typed(untpd.TypeTree(hi1.typeOpt.bottomType)) - val hi2 = if (!hi1.isEmpty) hi1 else typed(untpd.TypeTree(lo1.typeOpt.topType)) + val lo2 = if (lo1.isEmpty) typed(untpd.TypeTree(hi1.typeOpt.bottomType)) else lo1 + val hi2 = if (hi1.isEmpty) typed(untpd.TypeTree(lo1.typeOpt.topType)) else hi1 val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo2, hi2), lo2, hi2) if (ctx.mode.is(Mode.Pattern)) { From 62b773292d95399d86f239e7e543537d323c650f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2017 10:51:29 +0200 Subject: [PATCH 27/32] Add documentation. --- .../tools/dotc/core/PhantomErasure.scala | 28 +++++++++++++++++++ .../dotty/tools/dotc/core/TypeErasure.scala | 5 ++-- .../src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/transform/Erasure.scala | 4 +-- .../src/dotty/tools/dotc/typer/Typer.scala | 1 + 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/core/PhantomErasure.scala diff --git a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala new file mode 100644 index 000000000000..b75d71cdf65a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala @@ -0,0 +1,28 @@ +package dotty.tools.dotc.core + +import dotty.tools.dotc.ast.tpd._ +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Types.Type + +/** Phantom erasure erases (minimal erasure): + * + * - Parameters/arguments are erased to BoxedUnit. The next step will remove the parameters + * from the method definitions and calls (implemented in branch implement-phantom-types-part-2). + * - Definitions of `def`, `val`, `lazy val` and `var` returning a phantom type to return a BoxedUnit. The next step + * is to erase the fields for phantom types (implemented in branch implement-phantom-types-part-3) + * - Calls to Phantom.assume become calls to BoxedUnit. Intended to be optimized away by local optimizations. + * + * BoxedUnit is used as it fits perfectly and homogeneously in all locations where phantoms can be found. + * Additionally some of the optimizations that can be performed on phantom types can also be directly implemented + * on all boxed units. + */ +object PhantomErasure { + + /** Returns the default erased type of a phantom type */ + def erasedPhantomType(implicit ctx: Context): Type = defn.BoxedUnitType + + /** Returns the default erased tree for a call to Phantom.assume */ + def erasedAssume(implicit ctx: Context): Tree = ref(defn.BoxedUnit_UNIT) + +} diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 5f8fcf243e40..5f3e1b5737bf 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -364,7 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym) - else if (defn.isPhantomTerminalClass(tp.symbol)) defn.BoxedUnitType + else if (defn.isPhantomTerminalClass(tp.symbol)) PhantomErasure.erasedPhantomType else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -404,6 +404,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case tr :: trs1 => assert(!tr.classSymbol.is(Trait), cls) val tr1 = if (cls is Trait) defn.ObjectType else tr + // We remove the Phantom trait to erase the definitions of Phantom.{assume, Any, Nothing} tr1 :: trs1.filterNot(x => x.isRef(defn.ObjectClass) || x.isRef(defn.PhantomClass)) case nil => nil } @@ -508,7 +509,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (defn.isSyntheticFunctionClass(sym)) sigName(defn.erasedFunctionType(sym)) else if (defn.isPhantomTerminalClass(tp.symbol)) - sigName(defn.BoxedUnitType) + sigName(PhantomErasure.erasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayOf(elem) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index e2084cfdf9df..4191251e260a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -201,7 +201,7 @@ object Types { else defn.NothingType } - /** Returns the type of the lattice (i.e. the prefix of the phantom type) + /** Returns the type of the phantom lattice (i.e. the prefix of the phantom type) * - XYZ if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - NoType otherwise */ diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index fe73b42eb735..777e203fe19f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -23,7 +23,7 @@ import core.Decorators._ import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.{Constants, Flags} +import core.{Constants, Flags, Mode, PhantomErasure} import ValueClasses._ import TypeUtils._ import ExplicitOuter._ @@ -455,7 +455,7 @@ object Erasure extends TypeTestsCasts{ if (fun.symbol == defn.cbnArg) typedUnadapted(args.head, pt) else if (fun.symbol eq defn.Phantom_assume) - ref(defn.BoxedUnit_UNIT) + PhantomErasure.erasedAssume else typedExpr(fun, FunProto(args, pt, this)) match { case fun1: Apply => // arguments passed in prototype were already passed fun1 diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6c618dffb70e..3edfb09fbcf8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1334,6 +1334,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cls, isRequired, cdef.pos) } + // Check that phantom lattices are defined in an `object` if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass)) { if (!cls.is(Module)) ctx.error("Only object can extend scala.Phantom", cdef.pos) From 4202b9815f6163a3c4cb1951c7f506011339cddb Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2017 10:52:24 +0200 Subject: [PATCH 28/32] Fix neg/cycles.scala infinite loop on phantomLatticeType. --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 4191251e260a..63d52b1ee76a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -207,7 +207,7 @@ object Types { */ private final def phantomLatticeType(implicit ctx: Context): Type = widen match { case tp: ClassInfo if defn.isPhantomTerminalClass(tp.classSymbol) => tp.prefix - case tp: TypeProxy if tp.underlying ne this => tp.underlying.phantomLatticeType + case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType case tp: AndOrType => tp.tp1.phantomLatticeType case _ => NoType } From c6fe778172a8d3c6542e45b6f761d9ef359afaa4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2017 10:54:07 +0200 Subject: [PATCH 29/32] Code cleanup. --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 63d52b1ee76a..9a25da62be1c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -185,9 +185,9 @@ object Types { * - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - scala.Any otherwise */ - final def topType(implicit ctx: Context): TypeRef = { + final def topType(implicit ctx: Context): Type = { val lattice = phantomLatticeType - if (lattice.exists) lattice.select(tpnme.Any).asInstanceOf[TypeRef] + if (lattice.exists) lattice.select(tpnme.Any) else defn.AnyType } @@ -197,7 +197,7 @@ object Types { */ final def bottomType(implicit ctx: Context): Type = { val lattice = phantomLatticeType - if (lattice.exists) lattice.select(tpnme.Nothing).asInstanceOf[TypeRef] + if (lattice.exists) lattice.select(tpnme.Nothing) else defn.NothingType } From 07c02c8c0dee0dbd44bc2cd97d32f43036640b94 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2017 11:47:00 +0200 Subject: [PATCH 30/32] Fix after rebase, if/match missmatch on phantoms. --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ec0fbeda7208..e0228431254d 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -418,8 +418,9 @@ trait TypeAssigner { tree.withType(avoidingType(expansion, bindings)) def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { - checkSameUniverse(thenp, elsep, "be combined in branches of if/else", tree.pos) - tree.withType(thenp.tpe | elsep.tpe) + val sameUniverse = checkSameUniverse(thenp, elsep, "be combined in branches of if/else", tree.pos) + val tpe = if (sameUniverse) thenp.tpe | elsep.tpe else thenp.tpe + tree.withType(tpe) } def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = @@ -433,11 +434,11 @@ trait TypeAssigner { def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { if (tree.selector.typeOpt.isPhantom) ctx.error("Cannot pattern match on phantoms", tree.selector.pos) - if (cases.nonEmpty) { - val head = cases.head - cases.tail.foreach(c => checkSameUniverse(head, c, "be combined in branches of a match", c.pos)) + val sameUniverse = cases.isEmpty || cases.tail.forall { c => + checkSameUniverse(cases.head, c, "be combined in branches of a match", c.pos) } - tree.withType(ctx.typeComparer.lub(cases.tpes)) + val tpe = if (sameUniverse) ctx.typeComparer.lub(cases.tpes) else cases.head.tpe + tree.withType(tpe) } def assignType(tree: untpd.Return)(implicit ctx: Context) = @@ -545,9 +546,10 @@ trait TypeAssigner { def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = tree.withType(pid.symbol.valRef) - private def checkSameUniverse(tree1: Tree, tree2: Tree, relationship: => String, pos: Position)(implicit ctx: Context) = { - if (tree1.tpe.topType != tree2.tpe.topType) - ctx.error(ex"${tree1.tpe} and ${tree2.tpe} are in different universes. They cannot $relationship", pos) + private def checkSameUniverse(tree1: Tree, tree2: Tree, relationship: => String, pos: Position)(implicit ctx: Context): Boolean = { + val sameUniverse = tree1.tpe.topType == tree2.tpe.topType + if (!sameUniverse) ctx.error(ex"${tree1.tpe} and ${tree2.tpe} are in different universes. They cannot $relationship", pos) + sameUniverse } } From 40ab53a5e308089f4d7b8055e0c0c280fd14ad9d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 May 2017 17:11:47 +0200 Subject: [PATCH 31/32] Polishings of Typer and TypeAssigner --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 58 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 10 +--- tests/neg/phantom-AndOr.scala | 4 +- tests/neg/phantom-expr.scala | 18 +++--- tests/neg/phantom-multiversal-AndOr.scala | 4 +- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index e0228431254d..970b70be5679 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -417,11 +417,8 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { - val sameUniverse = checkSameUniverse(thenp, elsep, "be combined in branches of if/else", tree.pos) - val tpe = if (sameUniverse) thenp.tpe | elsep.tpe else thenp.tpe - tree.withType(tpe) - } + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = + tree.withType(lubInSameUniverse(thenp :: elsep :: Nil, "branches of an if/else")) def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = tree.withType( @@ -433,12 +430,8 @@ trait TypeAssigner { def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { if (tree.selector.typeOpt.isPhantom) - ctx.error("Cannot pattern match on phantoms", tree.selector.pos) - val sameUniverse = cases.isEmpty || cases.tail.forall { c => - checkSameUniverse(cases.head, c, "be combined in branches of a match", c.pos) - } - val tpe = if (sameUniverse) ctx.typeComparer.lub(cases.tpes) else cases.head.tpe - tree.withType(tpe) + ctx.error("cannot pattern match on values of a phantom type", tree.selector.pos) + tree.withType(lubInSameUniverse(cases, "branches of a match")) } def assignType(tree: untpd.Return)(implicit ctx: Context) = @@ -446,7 +439,7 @@ trait TypeAssigner { def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = if (cases.isEmpty) tree.withType(expr.tpe) - else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes)) + else tree.withType(lubInSameUniverse(expr :: cases, "branches of a try")) def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = { val ownType = tree match { @@ -459,15 +452,11 @@ trait TypeAssigner { def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = tree.withType(ref.tpe) - def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = { - checkSameUniverse(left, right, "be combined in `&`", tree.pos) - tree.withType(left.tpe & right.tpe) - } + def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(inSameUniverse(_ & _, left.tpe, right, "an `&`")) - def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = { - checkSameUniverse(left, right, "be combined in `|`", tree.pos) - tree.withType(left.tpe | right.tpe) - } + def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(inSameUniverse(_ | _, left.tpe, right, "an `|`")) /** Assign type of RefinedType. * Refinements are typed as if they were members of refinement class `refineCls`. @@ -497,10 +486,10 @@ trait TypeAssigner { def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = tree.withType(ExprType(result.tpe)) - def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = { - checkSameUniverse(lo, hi, "be combined in type bounds.", tree.pos) - tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe)) - } + def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = + tree.withType( + if (lo eq hi) TypeAlias(lo.tpe) + else inSameUniverse(TypeBounds(_, _), lo.tpe, hi, "type bounds")) def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) = tree.withType(NamedType.withFixedSym(NoPrefix, sym)) @@ -546,12 +535,23 @@ trait TypeAssigner { def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = tree.withType(pid.symbol.valRef) - private def checkSameUniverse(tree1: Tree, tree2: Tree, relationship: => String, pos: Position)(implicit ctx: Context): Boolean = { - val sameUniverse = tree1.tpe.topType == tree2.tpe.topType - if (!sameUniverse) ctx.error(ex"${tree1.tpe} and ${tree2.tpe} are in different universes. They cannot $relationship", pos) - sameUniverse - } + /** Ensure that `tree2`'s type is in the same universe as `tree1`. If that's the case, return + * `op` applied to both types. + * If not, issue an error and return `tree1`'s type. + */ + private def inSameUniverse(op: (Type, Type) => Type, tp1: Type, tree2: Tree, relationship: => String)(implicit ctx: Context): Type = + if (tp1.topType == tree2.tpe.topType) + op(tp1, tree2.tpe) + else { + ctx.error(ex"$tp1 and ${tree2.tpe} are in different universes. They cannot be combined in $relationship", tree2.pos) + tp1 + } + private def lubInSameUniverse(trees: List[Tree], relationship: => String)(implicit ctx: Context): Type = + trees match { + case first :: rest => (first.tpe /: rest)(inSameUniverse(_ | _, _, _, relationship)) + case Nil => defn.NothingType + } } object TypeAssigner extends TypeAssigner diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3edfb09fbcf8..bc5d3c882bda 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1334,13 +1334,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cls, isRequired, cdef.pos) } - // Check that phantom lattices are defined in an `object` - if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass)) { - if (!cls.is(Module)) - ctx.error("Only object can extend scala.Phantom", cdef.pos) - else if (!cls.owner.is(Module) && !cls.owner.is(Package)) - ctx.error("An object extending scala.Phantom must be a top level object or in another object.", cdef.pos) - } + // Check that phantom lattices are defined in a static object + if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner) + ctx.error("only static objects can extend scala.Phantom", cdef.pos) // check value class constraints checkDerivedValueClass(cls, body1) diff --git a/tests/neg/phantom-AndOr.scala b/tests/neg/phantom-AndOr.scala index bb52563502b1..934bf037e51b 100644 --- a/tests/neg/phantom-AndOr.scala +++ b/tests/neg/phantom-AndOr.scala @@ -3,13 +3,13 @@ class BooFunDef1 { import Boo._ def fun1(b: BooAny | Any) = ??? // error - def fun2(b: BooAny | Any | Any) = ??? // error + def fun2(b: BooAny | Any | Any) = ??? // error // 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 fun7(b: BooAny & Any & Any) = ??? // error // error def fun8(b: Any & Any & BooAny) = ??? // error } diff --git a/tests/neg/phantom-expr.scala b/tests/neg/phantom-expr.scala index 1ebe92e30792..eff415121dae 100644 --- a/tests/neg/phantom-expr.scala +++ b/tests/neg/phantom-expr.scala @@ -11,16 +11,16 @@ class Foo { val b = true def fooIf1 = - if (b) blinky // error - else "" + if (b) blinky + else "" // error def fooIf2 = - if (b) "" // error - else blinky + if (b) "" + else blinky // error def fooIf3 = - if (b) boo1 // error - else blinky + if (b) boo1 + else blinky // error def fooMatch1 = blinky match { // error case _: Blinky => () @@ -32,6 +32,10 @@ class Foo { case 1 => boo1 case _ => blinky // error } + def fooTry = + try 1 + catch { case ex: Exception => blinky // error + } } object Boo extends Phantom { @@ -42,4 +46,4 @@ object Boo extends Phantom { object Boo1 extends Phantom { type Boo1Any = this.Any def boo1: Boo1Any = assume -} \ No newline at end of file +} diff --git a/tests/neg/phantom-multiversal-AndOr.scala b/tests/neg/phantom-multiversal-AndOr.scala index 2dee5295c99b..9fc8ff8bc6df 100644 --- a/tests/neg/phantom-multiversal-AndOr.scala +++ b/tests/neg/phantom-multiversal-AndOr.scala @@ -5,11 +5,11 @@ class BooFunDef1 { def fun1(b: One | A) = ??? // error def fun2(b: A | One) = ??? // error - def fun3(b: A | One | Any) = ??? // error + def fun3(b: A | One | Any) = ??? // error // error def fun4(b: A & One) = ??? // error def fun5(b: One & A) = ??? // error - def fun6(b: A & One & Any) = ??? // error + def fun6(b: A & One & Any) = ??? // error // error } object Universe1 extends Phantom { From a21404ab4acb7159bd9b36a86c0d15d68d8cfab2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 8 May 2017 19:01:34 +0200 Subject: [PATCH 32/32] Remove unused error messages. --- .../reporting/diagnostic/ErrorMessageID.java | 9 -- .../dotc/reporting/diagnostic/messages.scala | 82 +------------------ .../dotty/tools/dotc/transform/Erasure.scala | 3 +- 3 files changed, 3 insertions(+), 91 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 427b44468c39..73bec87c01df 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -57,15 +57,6 @@ public enum ErrorMessageID { CyclicReferenceInvolvingImplicitID, SuperQualMustBeParentID, AmbiguousImportID, - ErasedPhantomsSignatureCollisionID, - PhantomInheritanceID, - PhantomMixedBoundsID, - PhantomCrossedMixedBoundsID, - MatchPhantomID, - MatchOnPhantomID, - IfElsePhantomID, - PhantomIsInObjectID, - PhantomObjectIsInPackageOrObjectID, ; 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 7bfa9c461725..aa70443db42e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -135,7 +135,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 @@ -1252,85 +1252,5 @@ object messages { | ${"import"} scala.{ $name => ${name.show + "Tick"} } |""" } - - 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(tpdCase: tpd.CaseDef, expected: Type)(implicit ctx: Context) extends Message(MatchPhantomID) { - val kind = "Phantom restriction" - val msg = s"Pattern expected case to return a ${expected.show} but was ${tpdCase.tpe.show}" - - 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(thenp: tpd.Tree, elsep: tpd.Tree)(implicit ctx: Context) extends Message(IfElsePhantomID) { - val kind = "Phantom restriction" - val msg = - s"""if/else cannot have branches with types in different lattices: - | ${thenp.tpe.show} of lattice ${thenp.tpe.topType.show} - | ${elsep.tpe.show} of lattice ${elsep.tpe.topType.show} - """.stripMargin - - 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/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 777e203fe19f..cc41ce00329e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -23,11 +23,12 @@ import core.Decorators._ import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ import scala.collection.mutable.ListBuffer -import core.{Constants, Flags, Mode, PhantomErasure} +import dotty.tools.dotc.core.{Constants, Flags} import ValueClasses._ import TypeUtils._ import ExplicitOuter._ import core.Mode +import core.PhantomErasure class Erasure extends Phase with DenotTransformer { thisTransformer =>