diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5c01c8ca59d1..93541eda4bf1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -60,14 +60,14 @@ class Definitions { private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered - private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) + private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) = + scope.enter(newSymbol(cls, name, flags, typeBounds)) - private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = - enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) + private def enterTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope, typeBounds: TypeBounds) = + enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope, typeBounds) private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") = - enterTypeParam(cls, suffix.toTypeName.expandedName(cls), paramFlags, scope) + enterTypeParam(cls, suffix.toTypeName.expandedName(cls), paramFlags, scope, TypeBounds.empty) // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only // implemented in Dotty and not in Scala 2. @@ -108,32 +108,30 @@ class Definitions { * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R * } */ - def newFunctionNTrait(name: TypeName): ClassSymbol = { + def newFunctionNTrait(name: TypeName, lattice: Symbol): ClassSymbol = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope val arity = name.functionArity + val top = lattice.thisType.select(tpnme.Any) + val bottom = lattice.thisType.select(tpnme.Nothing) val argParams = for (i <- List.range(0, arity)) yield - enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls) - val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls) + enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls, TypeBounds(bottom, top)).typeRef + val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls, TypeBounds.empty).typeRef val (methodType, parentTraits) = if (name.firstPart.startsWith(str.ImplicitFunction)) { val superTrait = - FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) + FunctionType(arity, isImplicit = false, top).appliedTo(argParams ::: resParam :: Nil) (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) } else (MethodType, Nil) - val applyMeth = - decls.enter( - newMethod(cls, nme.apply, - methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) - denot.info = - ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls) + decls.enter(newMethod(cls, nme.apply, methodType(argParams, resParam), Deferred)) + denot.info = ClassInfo(lattice.thisType, cls, ObjectType :: parentTraits, decls) } } - newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer) + newClassSymbol(lattice, name, Trait | NoInits, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = @@ -193,7 +191,7 @@ class Definitions { val cls = ScalaPackageVal.moduleClass.asClass cls.info.decls.openForMutations.useSynthesizer( name => ctx => - if (name.isTypeName && name.isSyntheticFunction) newFunctionNTrait(name.asTypeName) + if (name.isTypeName && name.isSyntheticScalaFunction) newFunctionNTrait(name.asTypeName, cls) else NoSymbol) cls } @@ -654,7 +652,7 @@ class Definitions { object FunctionOf { def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) = - FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil) + FunctionType(args, resultType, isImplicit).appliedTo(args ::: resultType :: Nil) def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol if (isFunctionClass(tsym)) { @@ -719,9 +717,27 @@ class Definitions { lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply) def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol - def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef = - if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n) + def FunctionType(n: Int, isImplicit: Boolean = false, top: Type = AnyType)(implicit ctx: Context): TypeRef = { + if (top.isPhantom) { + val functionPrefix = if (isImplicit) str.ImplicitFunction else str.Function + val functionName = (functionPrefix + n).toTypeName + val functionType = top.normalizedPrefix.select(functionName) + assert(functionType.classSymbol.exists) + functionType.asInstanceOf[TypeRef] + } + else if (n <= MaxImplementedFunctionArity && (!isImplicit || ctx.erasedTypes)) ImplementedFunctionType(n) else FunctionClass(n, isImplicit).typeRef + } + + def FunctionType(args: List[Type], resultType: Type, isImplicit: Boolean)(implicit ctx: Context): TypeRef = + FunctionType(args.length, isImplicit, topInSameUniverse(args, "function arguments.")) + + private def topInSameUniverse(types: List[Type], relationship: => String)(implicit ctx: Context): Type = { + types match { + case first :: rest => (first /: rest)(inSameUniverse((t1, _) => t1.topType, _, _, relationship, ctx.owner.pos)) + case Nil => defn.AnyType + } + } private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet @@ -729,6 +745,10 @@ class Definitions { def scalaClassName(cls: Symbol)(implicit ctx: Context): TypeName = if (cls.isClass && cls.owner == ScalaPackageClass) cls.asClass.name else EmptyTypeName + /** If `cls` is a class in an object extending scala.Phantom, its name, otherwise EmptyTypeName */ + def phantomClassName(cls: Symbol)(implicit ctx: Context): TypeName = + if (cls.isClass && cls.owner.derivesFrom(PhantomClass)) cls.asClass.name else EmptyTypeName + /** If type `ref` refers to a class in the scala package, its name, otherwise EmptyTypeName */ def scalaClassName(ref: Type)(implicit ctx: Context): TypeName = scalaClassName(ref.classSymbol) @@ -747,24 +767,28 @@ class Definitions { * - FunctionN for N >= 0 * - ImplicitFunctionN for N >= 0 */ - def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction + def isFunctionClass(cls: Symbol) = + scalaClassName(cls).isFunction || phantomClassName(cls).isFunction /** Is an implicit function class. * - ImplicitFunctionN for N >= 0 */ - def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction + def isImplicitFunctionClass(cls: Symbol) = + scalaClassName(cls).isImplicitFunction || phantomClassName(cls).isImplicitFunction /** Is a class that will be erased to FunctionXXL * - FunctionN for N >= 22 * - ImplicitFunctionN for N >= 22 */ - def isXXLFunctionClass(cls: Symbol) = scalaClassName(cls).functionArity > MaxImplementedFunctionArity + def isXXLFunctionClass(cls: Symbol) = + scalaClassName(cls).functionArity > MaxImplementedFunctionArity /** Is a synthetic function class * - FunctionN for N > 22 * - ImplicitFunctionN for N >= 0 */ - def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction + def isSyntheticFunctionClass(cls: Symbol) = + scalaClassName(cls).isSyntheticScalaFunction || phantomClassName(cls).isFunction def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, str.AbstractFunction) def isTupleClass(cls: Symbol) = isVarArityClass(cls, str.Tuple) @@ -781,6 +805,7 @@ class Definitions { val arity = scalaClassName(cls).functionArity if (arity > 22) defn.FunctionXXLClass else if (arity >= 0) defn.FunctionClass(arity) + else if (phantomClassName(cls).isFunction) defn.FunctionClass(0) else NoSymbol } @@ -795,6 +820,7 @@ class Definitions { val arity = scalaClassName(cls).functionArity if (arity > 22) defn.FunctionXXLType else if (arity >= 0) defn.FunctionType(arity) + else if (phantomClassName(cls).isFunction) defn.FunctionType(0) else NoType } @@ -839,7 +865,10 @@ class Definitions { * trait gets screwed up. Therefore, it is mandatory that FunctionXXL * is treated as a NoInit trait. */ - lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass + private lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass + + def isNoInitClass(cls: Symbol): Boolean = + cls.is(NoInitsTrait) || NoInitClasses.contains(cls) || isFunctionClass(cls) def isPolymorphicAfterErasure(sym: Symbol) = (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) @@ -860,7 +889,11 @@ class Definitions { def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) val sym = tp.dealias.typeSymbol - arity >= 0 && isFunctionClass(sym) && tp.isRef(FunctionType(arity, sym.name.isImplicitFunction).typeSymbol) + def top = + if (!sym.owner.derivesFrom(defn.PhantomClass)) defn.AnyType + else sym.owner.thisType.select(tpnme.Any) + def funType = FunctionType(arity, sym.name.isImplicitFunction, top) + arity >= 0 && isFunctionClass(sym) && tp.isRef(funType.typeSymbol) } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 @@ -975,12 +1008,27 @@ class Definitions { // ----- Phantoms --------------------------------------------------------- lazy val PhantomClass: ClassSymbol = { - val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType))) + val clsScope = newScope.openForMutations + val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType), clsScope)) val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil) val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef)) enterMethod(cls, nme.assume_, MethodType(Nil, nothing.typeRef), Protected | Final | Method) + clsScope.useSynthesizer { name => ctx => + if (name.isTypeName && name.isFunction) newFunctionNTrait(name.asTypeName, cls) + else NoSymbol + } + + if (config.Config.useFingerPrints) { + // FIXME: this should not be required, must create them early to make sure they are + // found in the members of objects extending scala.Phantom + for (i <- 1 to MaxImplementedFunctionArity) { + clsScope.lookup((str.Function + i).toTypeName) + clsScope.lookup((str.ImplicitFunction + i).toTypeName) + } + } + cls } lazy val Phantom_AnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass @@ -990,4 +1038,16 @@ class Definitions { /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass) + /** Ensure that `tp2`' is in the same universe as `tp1`. If that's the case, return + * `op` applied to both types. + * If not, issue an error and return `tp1`'. + */ + def inSameUniverse(op: (Type, Type) => Type, tp1: Type, tp2: Type, relationship: => String, pos: Position)(implicit ctx: Context): Type = + if (tp1.topType == tp2.topType) + op(tp1, tp2) + else { + ctx.error(ex"$tp1 and $tp2 are in different universes. They cannot be combined in $relationship", pos) + tp1 + } + } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index df29a32c523c..a66f445a5c41 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -154,7 +154,7 @@ object NameOps { } } - /** Is a synthetic function name + /** Is a function name * - N for FunctionN * - N for ImplicitFunctionN * - (-1) otherwise @@ -175,14 +175,14 @@ object NameOps { */ def isImplicitFunction: Boolean = functionArityFor(str.ImplicitFunction) >= 0 - /** Is a synthetic function name + /** Is a synthetic function name (in scala package) * - FunctionN for N > 22 * - ImplicitFunctionN for N >= 0 * - false otherwise */ - def isSyntheticFunction: Boolean = { + def isSyntheticScalaFunction: Boolean = { functionArityFor(str.Function) > MaxImplementedFunctionArity || - functionArityFor(str.ImplicitFunction) >= 0 + functionArityFor(str.ImplicitFunction) >= 0 } /** Parsed function arity for function with some specific prefix */ diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index f90778df009a..a1d75ba0db82 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -176,7 +176,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => case Some(call) => if (defn.NotRuntimeClasses.contains(baseCls)) Nil else call :: Nil case None => - if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil + if (defn.isNoInitClass(baseCls)) Nil else { //println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}") transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 93bd2528c380..30f45eeb3913 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -453,10 +453,10 @@ trait TypeAssigner { tree.withType(ref.tpe) def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(inSameUniverse(_ & _, left.tpe, right, "an `&`")) + tree.withType(defn.inSameUniverse(_ & _, left.tpe, right.tpe, "an `&`", right.pos)) def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(inSameUniverse(_ | _, left.tpe, right, "an `|`")) + tree.withType(defn.inSameUniverse(_ | _, left.tpe, right.tpe, "an `|`", right.pos)) /** Assign type of RefinedType. * Refinements are typed as if they were members of refinement class `refineCls`. @@ -489,7 +489,7 @@ trait TypeAssigner { 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")) + else defn.inSameUniverse(TypeBounds(_, _), lo.tpe, hi.tpe, "type bounds", hi.pos)) def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) = tree.withType(NamedType.withFixedSym(NoPrefix, sym)) @@ -535,21 +535,9 @@ trait TypeAssigner { def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = tree.withType(pid.symbol.valRef) - /** 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 first :: rest => (first.tpe /: rest)((tp, tree) => defn.inSameUniverse(_ | _, tp, tree.tpe, relationship, tree.pos)) case Nil => defn.NothingType } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 69140fcd0204..d09b31ddc33a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -671,9 +671,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) { - val funCls = defn.FunctionClass(args.length, tree.isInstanceOf[untpd.ImplicitFunction]) - typed(cpy.AppliedTypeTree(tree)( - untpd.TypeTree(funCls.typeRef), args :+ body), pt) + val argsTypes = args.map(tr => typed(tr).tpe) + val funTpe = defn.FunctionType(argsTypes, typed(body).tpe, tree.isInstanceOf[untpd.ImplicitFunction]) + typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funTpe), args :+ body), pt) } else { val params = args.asInstanceOf[List[untpd.ValDef]] diff --git a/library/src/scala/Phantom.scala b/library/src/scala/Phantom.scala index 4454a64cfe51..7ccfb225868b 100644 --- a/library/src/scala/Phantom.scala +++ b/library/src/scala/Phantom.scala @@ -8,5 +8,20 @@ trait Phantom { protected final trait Nothing extends this.Any protected final def assume: this.Nothing + + trait Function1[-T1 <: this.Any, +R] { + def apply(x1: T1): R + } + trait ImplicitFunction1[-T1 <: this.Any, +R] extends Function1[T1, R] { + /*implicit*/ def apply(x1: T1): R + } + ... + trait FunctionN[-T1 <: this.Any, ..., -Tn <: this.Any, +R] { + def apply(x1: T1, ..., xn: Tn): R + } + trait ImplicitFunctionN[-T1 <: this.Any, ..., -Tn <: this.Any, +R] extends FunctionN[T1, ..., Tn, R] { + /*implicit*/ def apply(x1: T1, ..., xn: Tn): R + } + } */ diff --git a/tests/neg/phantom-Functions-1.scala b/tests/neg/phantom-Functions-1.scala new file mode 100644 index 000000000000..35d9c633ec06 --- /dev/null +++ b/tests/neg/phantom-Functions-1.scala @@ -0,0 +1,6 @@ + +class PhantomFun1NoApply extends Function1[Boo.Casper, Unit] // error: class PhantomFun1NoApply needs to be abstract, since def apply: (p0: Casper)Unit is not defined + +object Boo extends Phantom { + type Casper <: this.Any +} diff --git a/tests/neg/phantom-Functions-2.scala b/tests/neg/phantom-Functions-2.scala new file mode 100644 index 000000000000..de568ba781f2 --- /dev/null +++ b/tests/neg/phantom-Functions-2.scala @@ -0,0 +1,16 @@ + +class PhantomFun1 extends Boo.Function2[Boo.Casper, Int, Unit] { // error: Type argument Int does not conform to upper bound Boo.Any + def apply(x1: Boo.Casper, x2: Int): Unit = () +} + +class PhantomFun2 extends Boo.Function2[Int, Boo.Casper, Unit] { // error: Type argument Int does not conform to upper bound Boo.Any + def apply(x1: Boo.Casper, x2: Int): Unit = () +} + +class Fun extends Function2[Int, Int, Unit] { + def apply(x1: Int, x2: Int): Unit = () +} + +object Boo extends Phantom { + type Casper <: this.Any +} diff --git a/tests/neg/phantom-Functions-3.scala b/tests/neg/phantom-Functions-3.scala new file mode 100644 index 000000000000..4c87d1f4c96e --- /dev/null +++ b/tests/neg/phantom-Functions-3.scala @@ -0,0 +1,14 @@ + +// TODO: Importing an object that exends phantom makes FunctionN refere to the Boo.FunctionN +// We should be carefull with this. Use a waring when importing Boo._ or disallow it. +// Or put funtions in an inner object in the Phantom trait, for example scala.Phantom.Functions + +import Boo._ + +class Fun extends Function1[Int, Unit] { // error: Type argument Int does not conform to upper bound Boo.Any + def apply(x1: Int): Unit = () +} + +object Boo extends Phantom { + type Casper <: this.Any +} diff --git a/tests/neg/phantom-Lambda.scala b/tests/neg/phantom-Lambda.scala new file mode 100644 index 000000000000..b57d475eee60 --- /dev/null +++ b/tests/neg/phantom-Lambda.scala @@ -0,0 +1,16 @@ + +class Fun { + def a = (x1: Int, x2: Boo.B) => x1 // error: Int and Boo.B are in different universes. They cannot be combined in function arguments. + def b: (Int, Boo.B) => Unit = (x1, x2) => x1 // error: Int and Boo.B are in different universes. They cannot be combined in function arguments. + + def c = (x1: Foo.F, x2: Boo.B) => x1 // error: Foo.F and Boo.B are in different universes. They cannot be combined in function arguments. + def d: (Foo.F, Boo.B) => Unit = (x1, x2) => x1 // error: Foo.F and Boo.B are in different universes. They cannot be combined in function arguments. +} + +object Boo extends Phantom { + type B <: this.Any +} + +object Foo extends Phantom { + type F <: this.Any +} diff --git a/tests/run/phantom-Functions-0.check b/tests/run/phantom-Functions-0.check new file mode 100644 index 000000000000..ca91d311132e --- /dev/null +++ b/tests/run/phantom-Functions-0.check @@ -0,0 +1,3 @@ +Blinky1.apply() +Blinky1.apply() +Blinky1.apply() diff --git a/tests/run/phantom-Functions-0.scala b/tests/run/phantom-Functions-0.scala new file mode 100644 index 000000000000..e80949311ed8 --- /dev/null +++ b/tests/run/phantom-Functions-0.scala @@ -0,0 +1,23 @@ + +import Boo._ + +object Test { + + def main(args: Array[String]): Unit = { + (new Blinky1)(pinky) + foo1(pinky) + foo2(new Blinky1) + } + + def foo1: Pinky => Unit = new Blinky1 + def foo2(fun3: Function1[Pinky, Unit]) = fun3(pinky) +} + +class Blinky1 extends Function1[Pinky, Unit] { + def apply(p1: Pinky) = println("Blinky1.apply()") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-1.check b/tests/run/phantom-Functions-1.check new file mode 100644 index 000000000000..468962bc52b3 --- /dev/null +++ b/tests/run/phantom-Functions-1.check @@ -0,0 +1,2 @@ +Blinky2.apply() +Blinky2.apply() \ No newline at end of file diff --git a/tests/run/phantom-Functions-1.scala b/tests/run/phantom-Functions-1.scala new file mode 100644 index 000000000000..8666530a716d --- /dev/null +++ b/tests/run/phantom-Functions-1.scala @@ -0,0 +1,20 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky2)(pinky, pinky) + bar1(pinky, pinky) + } + val bar1: (Pinky, Pinky) => Unit = new Blinky2 + +} + +class Blinky2 extends Function2[Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky) = println("Blinky2.apply()") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-2.check b/tests/run/phantom-Functions-2.check new file mode 100644 index 000000000000..bd68aebc7445 --- /dev/null +++ b/tests/run/phantom-Functions-2.check @@ -0,0 +1,4 @@ +Blinky.apply() +Blinky.apply() +Blinky.apply() +Blinky.apply() diff --git a/tests/run/phantom-Functions-2.scala b/tests/run/phantom-Functions-2.scala new file mode 100644 index 000000000000..78b8f0740ea9 --- /dev/null +++ b/tests/run/phantom-Functions-2.scala @@ -0,0 +1,24 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + (new Blinky)(pinky, pinky) + (new Blinky).apply(pinky, pinky) + + foo1(pinky, pinky) + foo2(new Blinky) + } + + def foo1: (Pinky, Pinky) => Unit = new Blinky + def foo2(boo: Function2[Pinky, Pinky, Unit]) = boo(pinky, pinky) +} + +class Blinky extends Function2[Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky) = println("Blinky.apply()") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-3.check b/tests/run/phantom-Functions-3.check new file mode 100644 index 000000000000..b9c73b8cc42d --- /dev/null +++ b/tests/run/phantom-Functions-3.check @@ -0,0 +1 @@ +Blinky.apply() diff --git a/tests/run/phantom-Functions-3.scala b/tests/run/phantom-Functions-3.scala new file mode 100644 index 000000000000..6a66f8cb27f8 --- /dev/null +++ b/tests/run/phantom-Functions-3.scala @@ -0,0 +1,19 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(pinky, pinky, pinky, pinky, pinky) + } + + def foo3: (Pinky, Pinky, Pinky, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky, p3: Pinky, p4: Pinky, p5: Pinky) = println("Blinky.apply()") +} + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-4.check b/tests/run/phantom-Functions-4.check new file mode 100644 index 000000000000..b9c73b8cc42d --- /dev/null +++ b/tests/run/phantom-Functions-4.check @@ -0,0 +1 @@ +Blinky.apply() diff --git a/tests/run/phantom-Functions-4.scala b/tests/run/phantom-Functions-4.scala new file mode 100644 index 000000000000..93bd55d56199 --- /dev/null +++ b/tests/run/phantom-Functions-4.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(pinky, pinky, pinky, pinky, pinky) + } + + val foo3: (Pinky, Pinky, Pinky, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky + +class Blinky extends Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky, p3: Pinky, p4: Pinky, p5: Pinky) = println("Blinky.apply()") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-5.check b/tests/run/phantom-Functions-5.check new file mode 100644 index 000000000000..d1bbadb32c97 --- /dev/null +++ b/tests/run/phantom-Functions-5.check @@ -0,0 +1 @@ +Blinky2.apply() diff --git a/tests/run/phantom-Functions-5.scala b/tests/run/phantom-Functions-5.scala new file mode 100644 index 000000000000..c26ff9272302 --- /dev/null +++ b/tests/run/phantom-Functions-5.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(pinky, pinky, pinky, pinky, pinky) + } + + val foo3: (Pinky, Pinky, Pinky, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky { + def apply(p1: Pinky, p2: Pinky, p3: Pinky, p4: Pinky, p5: Pinky) = println("Blinky2.apply()") +} + +abstract class Blinky extends Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit] + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-6.check b/tests/run/phantom-Functions-6.check new file mode 100644 index 000000000000..d1bbadb32c97 --- /dev/null +++ b/tests/run/phantom-Functions-6.check @@ -0,0 +1 @@ +Blinky2.apply() diff --git a/tests/run/phantom-Functions-6.scala b/tests/run/phantom-Functions-6.scala new file mode 100644 index 000000000000..e9a3f7b45ae3 --- /dev/null +++ b/tests/run/phantom-Functions-6.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(pinky, pinky, pinky, pinky, pinky) + } + + val foo3: (Pinky, Pinky, Pinky, Pinky, Pinky) => Unit = new Blinky2 +} + +class Blinky2 extends Blinky { + def apply(p1: Pinky, p2: Pinky, p3: Pinky, p4: Pinky, p5: Pinky) = println("Blinky2.apply()") +} + +trait Blinky extends Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit] + +object Boo extends Phantom { + type Pinky <: this.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Functions-7.check b/tests/run/phantom-Functions-7.check new file mode 100644 index 000000000000..b9c73b8cc42d --- /dev/null +++ b/tests/run/phantom-Functions-7.check @@ -0,0 +1 @@ +Blinky.apply() diff --git a/tests/run/phantom-Functions-7.scala b/tests/run/phantom-Functions-7.scala new file mode 100644 index 000000000000..e742b7465939 --- /dev/null +++ b/tests/run/phantom-Functions-7.scala @@ -0,0 +1,21 @@ + +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + foo3(pinky, pinky, pinky, pinky, pinky) + } + + val foo3: (Pinky, Pinky, Pinky, Pinky, Pinky) => Unit = new Blinky2().asInstanceOf[Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit]] +} + +class Blinky2 extends Blinky + +trait Blinky extends Function5[Pinky, Pinky, Pinky, Pinky, Pinky, Unit] { + def apply(p1: Pinky, p2: Pinky, p3: Pinky, p4: Pinky, p5: Pinky) = println("Blinky.apply()") +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-ImplicitFunctions-0.scala b/tests/run/phantom-ImplicitFunctions-0.scala new file mode 100644 index 000000000000..3fa5f9b385c0 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-0.scala @@ -0,0 +1,62 @@ + +object Boo extends Phantom { + type Slimer <: this.Any + implicit def phantom: Slimer = assume +} + + +object Test { + + def main(args: Array[String]): Unit = { + implicit val world: String = "world!" + import Boo._ + + val i1 = (implicit (s: Slimer) => true) + val i2 = {implicit (s: Slimer) => false} + + assert(i1) + assert(!i2) + + val x: implicit (Slimer, Slimer) => Boolean = { implicit (s: Slimer, p: Slimer) => false } + + val xx: implicit (Slimer, Slimer, Slimer) => Int = implicit (x: Slimer, y: Slimer, p: Slimer) => 5 + + val y: (Slimer, Slimer) => Boolean = implicit (x: Slimer, y: Slimer) => true + + object nested { + implicit val empty: String = "" + assert(!x) + } + + val yy: (Slimer, Slimer, Slimer) => Any = xx + + val z1: implicit (Slimer, Slimer) => Boolean = true + assert(z1) + + type SlimyBool = implicit Slimer => Boolean + + // TODO: should support phantom implicitly? + // val z2: SlimyBool = { + // implicitly[Slimer] + // true + // } + // assert(z2) + + type GenericImplicit[X <: Slimer, Y <: Slimer] = implicit (X, Y) => Boolean + + val z4: GenericImplicit[Slimer, Slimer] = true + assert(z4) + + val b = x(phantom, phantom) + + val b1: Boolean = b + + val bi = x + + val bi1: Boolean = bi + + val c = xx(phantom, phantom, phantom) + + val c1: Int = c + } +} diff --git a/tests/run/phantom-ImplicitFunctions-1.check b/tests/run/phantom-ImplicitFunctions-1.check new file mode 100644 index 000000000000..8baef1b4abc4 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-1.check @@ -0,0 +1 @@ +abc diff --git a/tests/run/phantom-ImplicitFunctions-1.scala b/tests/run/phantom-ImplicitFunctions-1.scala new file mode 100644 index 000000000000..5f824a548098 --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-1.scala @@ -0,0 +1,18 @@ + +object Test { + import CanPrintPhantoms._ + + type Ctx[T] = implicit CanPrint => T + + def contextualPrintln(s: String): Ctx[Unit] = implicit canPrint => println(s) + + def main(args: Array[String]): Unit = { + implicit def ev: CanPrint = canPrint + contextualPrintln("abc") + } +} + +object CanPrintPhantoms extends Phantom { + type CanPrint <: this.Any + def canPrint: CanPrint = assume +} diff --git a/tests/run/phantom-ImplicitFunctions-2.check b/tests/run/phantom-ImplicitFunctions-2.check new file mode 100644 index 000000000000..893d9bc9e42c --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-2.check @@ -0,0 +1,5 @@ +result: 4 +******* log ******** +first step: 1 +second step: 2 +third step: 4 diff --git a/tests/run/phantom-ImplicitFunctions-2.scala b/tests/run/phantom-ImplicitFunctions-2.scala new file mode 100644 index 000000000000..23965b3befca --- /dev/null +++ b/tests/run/phantom-ImplicitFunctions-2.scala @@ -0,0 +1,51 @@ + +import collection.mutable.ListBuffer + +class Transaction { + private val log = new ListBuffer[String] + def println(s: String): Unit = log += s + + private var aborted = false + private var committed = false + + def abort(): Unit = { aborted = true } + def isAborted = aborted + + def commit(): Unit = + if (!aborted && !committed) { + Console.println("******* log ********") + log.foreach(Console.println) + committed = true + } +} + +object Test extends Phantom { + type CanDoTransaction <: this.Any + + def transaction[T](op: Transaction => CanDoTransaction => T) = { + val trans: Transaction = new Transaction + op(trans)(assume) + trans.commit() + } + + def f1(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"first step: $x") + f2(x + 1) + } + def f2(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"second step: $x") + f3(x * x) + } + def f3(x: Int)(implicit thisTransaction: Transaction, canDo: CanDoTransaction): Int = { + thisTransaction.println(s"third step: $x") + if (x % 2 != 0) thisTransaction.abort() + x + } + + def main(args: Array[String]) = { + transaction { implicit thisTransaction => implicit canDo => + val res = f1(args.length) + println(if (thisTransaction.isAborted) "aborted" else s"result: $res") + } + } +} diff --git a/tests/run/phantom-Lambda-0.check b/tests/run/phantom-Lambda-0.check new file mode 100644 index 000000000000..f70d7bba4ae1 --- /dev/null +++ b/tests/run/phantom-Lambda-0.check @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/tests/run/phantom-Lambda-0.scala b/tests/run/phantom-Lambda-0.scala new file mode 100644 index 000000000000..c956f3080fee --- /dev/null +++ b/tests/run/phantom-Lambda-0.scala @@ -0,0 +1,13 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + (((b, b2) => println(42)): ((Pinky, Pinky) => Unit)).apply(pinky, pinky) + } +} + +object Boo extends Phantom { + type Pinky <: Boo.Any + def pinky: Pinky = assume +} diff --git a/tests/run/phantom-Lambda-1.check b/tests/run/phantom-Lambda-1.check new file mode 100644 index 000000000000..463021151d29 --- /dev/null +++ b/tests/run/phantom-Lambda-1.check @@ -0,0 +1,2 @@ +foo1 +foo2 diff --git a/tests/run/phantom-Lambda-1.scala b/tests/run/phantom-Lambda-1.scala new file mode 100644 index 000000000000..f17ad89f896d --- /dev/null +++ b/tests/run/phantom-Lambda-1.scala @@ -0,0 +1,17 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + foo1(casper) + foo2(casper) + } + + def foo1: Casper => Unit = b => println("foo1") + def foo2 = (b: Casper) => println("foo2") +} + +object Boo extends Phantom { + type Casper <: Boo.Any + def casper: Casper = assume +} diff --git a/tests/run/phantom-Lambda-2.check b/tests/run/phantom-Lambda-2.check new file mode 100644 index 000000000000..45679f5c2d60 --- /dev/null +++ b/tests/run/phantom-Lambda-2.check @@ -0,0 +1,2 @@ +bar1 +bar2 diff --git a/tests/run/phantom-Lambda-2.scala b/tests/run/phantom-Lambda-2.scala new file mode 100644 index 000000000000..25edc814ce91 --- /dev/null +++ b/tests/run/phantom-Lambda-2.scala @@ -0,0 +1,20 @@ + +object Test { + import Boo._ + + def main(args: Array[String]) = { + val foo = (b: Casper, b2: Casper) => 42 + foo(casper, casper) + + bar1(casper, casper) + bar2(casper, casper) + } + + val bar1: (Casper, Casper) => Unit = (b, b1) => println("bar1") + val bar2 = (b: Casper, b2: Casper) => println("bar2") +} + +object Boo extends Phantom { + type Casper <: Boo.Any + def casper: Casper = assume +} diff --git a/tests/run/phantom-anonimous-functions.check b/tests/run/phantom-anonimous-functions.check new file mode 100644 index 000000000000..ecdf81daa328 --- /dev/null +++ b/tests/run/phantom-anonimous-functions.check @@ -0,0 +1,3 @@ +AnnonPhantomsFunction1.apply() 1 +AnnonPhantomsFunction1.apply() 2 +AnnonPhantomsFunction1.apply() 3 diff --git a/tests/run/phantom-anonimous-functions.scala b/tests/run/phantom-anonimous-functions.scala new file mode 100644 index 000000000000..0bd2343ce442 --- /dev/null +++ b/tests/run/phantom-anonimous-functions.scala @@ -0,0 +1,25 @@ + +object Test { + import Boo._ + + def main(args: Array[String]): Unit = { + val anonPhantomsFunction1 = new Function1[Blinky, Unit] { + def apply(p1: Blinky) = println("AnnonPhantomsFunction1.apply() 1") + } + anonPhantomsFunction1(boo[Blinky]) + + (new Function1[Blinky, Unit] { + def apply(p1: Blinky) = println("AnnonPhantomsFunction1.apply() 2") + }).apply(boo[Blinky]) + + (new Function2[Blinky, Blinky, Unit] { + def apply(p1: Blinky, p2: Blinky) = println("AnnonPhantomsFunction1.apply() 3") + }).apply(boo[Blinky], boo[Blinky]) + + } +} + +object Boo extends Phantom { + type Blinky <: this.Any + def boo[B <: Blinky]: B = assume +}