Skip to content

Commit a391a58

Browse files
authored
Merge pull request #2136 from dotty-staging/implement-phantom-types-part-1
Implement phantom types part 1
2 parents 7b1cdbc + a21404a commit a391a58

File tree

102 files changed

+1655
-32
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+1655
-32
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -670,16 +670,6 @@ object desugar {
670670
tree
671671
}
672672

673-
/** EmptyTree in lower bound ==> Nothing
674-
* EmptyTree in upper bounds ==> Any
675-
*/
676-
def typeBoundsTree(tree: TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = {
677-
val TypeBoundsTree(lo, hi) = tree
678-
val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo
679-
val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi
680-
cpy.TypeBoundsTree(tree)(lo1, hi1)
681-
}
682-
683673
/** Make closure corresponding to function.
684674
* params => body
685675
* ==>

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ class Definitions {
820820
lazy val UnqualifiedOwnerTypes: Set[NamedType] =
821821
RootImportTypes.toSet[NamedType] ++ RootImportTypes.map(_.symbol.moduleClass.typeRef)
822822

823-
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
823+
lazy val NotRuntimeClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
824824

825825
/** Classes that are known not to have an initializer irrespective of
826826
* whether NoInits is set. Note: FunctionXXLClass is in this set
@@ -832,7 +832,7 @@ class Definitions {
832832
* trait gets screwed up. Therefore, it is mandatory that FunctionXXL
833833
* is treated as a NoInit trait.
834834
*/
835-
lazy val NoInitClasses = PhantomClasses + FunctionXXLClass
835+
lazy val NoInitClasses = NotRuntimeClasses + FunctionXXLClass
836836

837837
def isPolymorphicAfterErasure(sym: Symbol) =
838838
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
@@ -936,7 +936,8 @@ class Definitions {
936936
NullClass,
937937
NothingClass,
938938
SingletonClass,
939-
EqualsPatternClass)
939+
EqualsPatternClass,
940+
PhantomClass)
940941

941942
lazy val syntheticCoreClasses = syntheticScalaClasses ++ List(
942943
EmptyPackageVal,
@@ -963,4 +964,23 @@ class Definitions {
963964
_isInitialized = true
964965
}
965966
}
967+
968+
// ----- Phantoms ---------------------------------------------------------
969+
970+
lazy val PhantomClass: ClassSymbol = {
971+
val cls = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.Phantom, NoInitsTrait, List(AnyType)))
972+
973+
val any = enterCompleteClassSymbol(cls, tpnme.Any, Protected | Final | NoInitsTrait, Nil)
974+
val nothing = enterCompleteClassSymbol(cls, tpnme.Nothing, Protected | Final | NoInitsTrait, List(any.typeRef))
975+
enterMethod(cls, nme.assume_, MethodType(Nil, nothing.typeRef), Protected | Final | Method)
976+
977+
cls
978+
}
979+
lazy val Phantom_AnyClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Any).asClass
980+
lazy val Phantom_NothingClass = PhantomClass.unforcedDecls.find(_.name eq tpnme.Nothing).asClass
981+
lazy val Phantom_assume = PhantomClass.unforcedDecls.find(_.name eq nme.assume_)
982+
983+
/** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */
984+
def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass)
985+
966986
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dotty.tools.dotc.core
2+
3+
import dotty.tools.dotc.ast.tpd._
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.Symbols.defn
6+
import dotty.tools.dotc.core.Types.Type
7+
8+
/** Phantom erasure erases (minimal erasure):
9+
*
10+
* - Parameters/arguments are erased to BoxedUnit. The next step will remove the parameters
11+
* from the method definitions and calls (implemented in branch implement-phantom-types-part-2).
12+
* - Definitions of `def`, `val`, `lazy val` and `var` returning a phantom type to return a BoxedUnit. The next step
13+
* is to erase the fields for phantom types (implemented in branch implement-phantom-types-part-3)
14+
* - Calls to Phantom.assume become calls to BoxedUnit. Intended to be optimized away by local optimizations.
15+
*
16+
* BoxedUnit is used as it fits perfectly and homogeneously in all locations where phantoms can be found.
17+
* Additionally some of the optimizations that can be performed on phantom types can also be directly implemented
18+
* on all boxed units.
19+
*/
20+
object PhantomErasure {
21+
22+
/** Returns the default erased type of a phantom type */
23+
def erasedPhantomType(implicit ctx: Context): Type = defn.BoxedUnitType
24+
25+
/** Returns the default erased tree for a call to Phantom.assume */
26+
def erasedAssume(implicit ctx: Context): Tree = ref(defn.BoxedUnit_UNIT)
27+
28+
}

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ object StdNames {
237237
final val SourceFileATTR: N = "SourceFile"
238238
final val SyntheticATTR: N = "Synthetic"
239239

240+
final val Phantom: N = "Phantom"
241+
240242
// ----- Term names -----------------------------------------
241243

242244
// Compiler-internal

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,8 @@ object SymDenotations {
483483
def isNumericValueClass(implicit ctx: Context) =
484484
maybeOwner == defn.ScalaPackageClass && defn.ScalaNumericValueClasses().contains(symbol)
485485

486-
/** Is symbol a phantom class for which no runtime representation exists? */
487-
def isPhantomClass(implicit ctx: Context) = defn.PhantomClasses contains symbol
486+
/** Is symbol a class for which no runtime representation exists? */
487+
def isNotRuntimeClass(implicit ctx: Context) = defn.NotRuntimeClasses contains symbol
488488

489489
/** Is this symbol a class representing a refinement? These classes
490490
* are used only temporarily in Typer and Unpickler as an intermediate
@@ -635,7 +635,7 @@ object SymDenotations {
635635

636636
/** Is this symbol a class references to which that are supertypes of null? */
637637
final def isNullableClass(implicit ctx: Context): Boolean =
638-
isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass
638+
isClass && !isValueClass && !(this is ModuleClass) && symbol != defn.NothingClass && !defn.isPhantomTerminalClass(symbol)
639639

640640
/** Is this definition accessible as a member of tree with type `pre`?
641641
* @param pre The type of the tree from which the selection is made

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
4747
private var myAnyClass: ClassSymbol = null
4848
private var myNothingClass: ClassSymbol = null
4949
private var myNullClass: ClassSymbol = null
50+
private var myPhantomNothingClass: ClassSymbol = null
5051
private var myObjectClass: ClassSymbol = null
5152
private var myAnyType: TypeRef = null
5253
private var myNothingType: TypeRef = null
@@ -63,6 +64,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
6364
if (myNullClass == null) myNullClass = defn.NullClass
6465
myNullClass
6566
}
67+
def PhantomNothingClass = {
68+
if (myPhantomNothingClass == null) myPhantomNothingClass = defn.Phantom_NothingClass
69+
myPhantomNothingClass
70+
}
6671
def ObjectClass = {
6772
if (myObjectClass == null) myObjectClass = defn.ObjectClass
6873
myObjectClass
@@ -550,8 +555,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
550555
case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2)
551556
case _ => false
552557
}
553-
(tp1.symbol eq NothingClass) && tp2.isValueTypeOrLambda ||
554-
(tp1.symbol eq NullClass) && isNullable(tp2)
558+
val sym1 = tp1.symbol
559+
(sym1 eq NothingClass) && tp2.isValueTypeOrLambda && !tp2.isPhantom ||
560+
(sym1 eq NullClass) && isNullable(tp2) ||
561+
(sym1 eq PhantomNothingClass) && tp1.topType == tp2.topType
555562
}
556563
case tp1: SingletonType =>
557564
/** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
364364
else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
365365
else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type.
366366
else if (defn.isSyntheticFunctionClass(sym)) defn.erasedFunctionType(sym)
367+
else if (defn.isPhantomTerminalClass(tp.symbol)) PhantomErasure.erasedPhantomType
367368
else eraseNormalClassRef(tp)
368369
case tp: RefinedType =>
369370
val parent = tp.parent
@@ -403,7 +404,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
403404
case tr :: trs1 =>
404405
assert(!tr.classSymbol.is(Trait), cls)
405406
val tr1 = if (cls is Trait) defn.ObjectType else tr
406-
tr1 :: trs1.filterNot(_ isRef defn.ObjectClass)
407+
// We remove the Phantom trait to erase the definitions of Phantom.{assume, Any, Nothing}
408+
tr1 :: trs1.filterNot(x => x.isRef(defn.ObjectClass) || x.isRef(defn.PhantomClass))
407409
case nil => nil
408410
}
409411
val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass)
@@ -506,6 +508,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
506508
}
507509
if (defn.isSyntheticFunctionClass(sym))
508510
sigName(defn.erasedFunctionType(sym))
511+
else if (defn.isPhantomTerminalClass(tp.symbol))
512+
sigName(PhantomErasure.erasedPhantomType)
509513
else
510514
normalizeClass(sym.asClass).fullName.asTypeName
511515
case defn.ArrayOf(elem) =>

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,44 @@ object Types {
172172
case _ =>
173173
false
174174
}
175-
cls == defn.AnyClass || loop(this)
175+
loop(this)
176+
}
177+
178+
/** Returns true if the type is a phantom type
179+
* - true if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any
180+
* - false otherwise
181+
*/
182+
final def isPhantom(implicit ctx: Context): Boolean = phantomLatticeType.exists
183+
184+
/** Returns the top type of the lattice
185+
* - XYX.Any if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any
186+
* - scala.Any otherwise
187+
*/
188+
final def topType(implicit ctx: Context): Type = {
189+
val lattice = phantomLatticeType
190+
if (lattice.exists) lattice.select(tpnme.Any)
191+
else defn.AnyType
192+
}
193+
194+
/** Returns the bottom type of the lattice
195+
* - XYZ.Nothing if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any
196+
* - scala.Nothing otherwise
197+
*/
198+
final def bottomType(implicit ctx: Context): Type = {
199+
val lattice = phantomLatticeType
200+
if (lattice.exists) lattice.select(tpnme.Nothing)
201+
else defn.NothingType
202+
}
203+
204+
/** Returns the type of the phantom lattice (i.e. the prefix of the phantom type)
205+
* - XYZ if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any
206+
* - NoType otherwise
207+
*/
208+
private final def phantomLatticeType(implicit ctx: Context): Type = widen match {
209+
case tp: ClassInfo if defn.isPhantomTerminalClass(tp.classSymbol) => tp.prefix
210+
case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType
211+
case tp: AndOrType => tp.tp1.phantomLatticeType
212+
case _ => NoType
176213
}
177214

178215
/** Is this type guaranteed not to have `null` as a value?

compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati
8989
case _ =>
9090
val elemType = tree.tpe.elemType
9191
var elemClass = elemType.classSymbol
92-
if (defn.PhantomClasses contains elemClass) elemClass = defn.ObjectClass
92+
if (defn.NotRuntimeClasses contains elemClass) elemClass = defn.ObjectClass
9393
ref(defn.DottyArraysModule)
9494
.select(nme.seqToArray)
9595
.appliedToType(elemType)

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import ValueClasses._
2828
import TypeUtils._
2929
import ExplicitOuter._
3030
import core.Mode
31+
import core.PhantomErasure
3132

3233
class Erasure extends Phase with DenotTransformer { thisTransformer =>
3334

@@ -454,6 +455,8 @@ object Erasure extends TypeTestsCasts{
454455
val Apply(fun, args) = tree
455456
if (fun.symbol == defn.cbnArg)
456457
typedUnadapted(args.head, pt)
458+
else if (fun.symbol eq defn.Phantom_assume)
459+
PhantomErasure.erasedAssume
457460
else typedExpr(fun, FunProto(args, pt, this)) match {
458461
case fun1: Apply => // arguments passed in prototype were already passed
459462
fun1

compiler/src/dotty/tools/dotc/transform/Mixin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
174174

175175
def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match {
176176
case Some(call) =>
177-
if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil
177+
if (defn.NotRuntimeClasses.contains(baseCls)) Nil else call :: Nil
178178
case None =>
179179
if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil
180180
else {

compiler/src/dotty/tools/dotc/transform/TreeGen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object TreeGen {
1414
def wrapArrayMethodName(elemtp: Type)(implicit ctx: Context): TermName = {
1515
val elemCls = elemtp.classSymbol
1616
if (elemCls.isPrimitiveValueClass) nme.wrapXArray(elemCls.name)
17-
else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isPhantomClass) nme.wrapRefArray
17+
else if (elemCls.derivesFrom(defn.ObjectClass) && !elemCls.isNotRuntimeClass) nme.wrapRefArray
1818
else nme.genericWrapArray
1919
}
2020

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ trait TypeAssigner {
418418
tree.withType(avoidingType(expansion, bindings))
419419

420420
def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) =
421-
tree.withType(thenp.tpe | elsep.tpe)
421+
tree.withType(lubInSameUniverse(thenp :: elsep :: Nil, "branches of an if/else"))
422422

423423
def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) =
424424
tree.withType(
@@ -428,15 +428,18 @@ trait TypeAssigner {
428428
def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) =
429429
tree.withType(body.tpe)
430430

431-
def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) =
432-
tree.withType(ctx.typeComparer.lub(cases.tpes))
431+
def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = {
432+
if (tree.selector.typeOpt.isPhantom)
433+
ctx.error("cannot pattern match on values of a phantom type", tree.selector.pos)
434+
tree.withType(lubInSameUniverse(cases, "branches of a match"))
435+
}
433436

434437
def assignType(tree: untpd.Return)(implicit ctx: Context) =
435438
tree.withType(defn.NothingType)
436439

437440
def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) =
438441
if (cases.isEmpty) tree.withType(expr.tpe)
439-
else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes))
442+
else tree.withType(lubInSameUniverse(expr :: cases, "branches of a try"))
440443

441444
def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = {
442445
val ownType = tree match {
@@ -450,10 +453,10 @@ trait TypeAssigner {
450453
tree.withType(ref.tpe)
451454

452455
def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
453-
tree.withType(left.tpe & right.tpe)
456+
tree.withType(inSameUniverse(_ & _, left.tpe, right, "an `&`"))
454457

455458
def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
456-
tree.withType(left.tpe | right.tpe)
459+
tree.withType(inSameUniverse(_ | _, left.tpe, right, "an `|`"))
457460

458461
/** Assign type of RefinedType.
459462
* Refinements are typed as if they were members of refinement class `refineCls`.
@@ -484,7 +487,9 @@ trait TypeAssigner {
484487
tree.withType(ExprType(result.tpe))
485488

486489
def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) =
487-
tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe))
490+
tree.withType(
491+
if (lo eq hi) TypeAlias(lo.tpe)
492+
else inSameUniverse(TypeBounds(_, _), lo.tpe, hi, "type bounds"))
488493

489494
def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) =
490495
tree.withType(NamedType.withFixedSym(NoPrefix, sym))
@@ -529,6 +534,24 @@ trait TypeAssigner {
529534

530535
def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) =
531536
tree.withType(pid.symbol.valRef)
537+
538+
/** Ensure that `tree2`'s type is in the same universe as `tree1`. If that's the case, return
539+
* `op` applied to both types.
540+
* If not, issue an error and return `tree1`'s type.
541+
*/
542+
private def inSameUniverse(op: (Type, Type) => Type, tp1: Type, tree2: Tree, relationship: => String)(implicit ctx: Context): Type =
543+
if (tp1.topType == tree2.tpe.topType)
544+
op(tp1, tree2.tpe)
545+
else {
546+
ctx.error(ex"$tp1 and ${tree2.tpe} are in different universes. They cannot be combined in $relationship", tree2.pos)
547+
tp1
548+
}
549+
550+
private def lubInSameUniverse(trees: List[Tree], relationship: => String)(implicit ctx: Context): Type =
551+
trees match {
552+
case first :: rest => (first.tpe /: rest)(inSameUniverse(_ | _, _, _, relationship))
553+
case Nil => defn.NothingType
554+
}
532555
}
533556

534557
object TypeAssigner extends TypeAssigner

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,10 +1108,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11081108
}
11091109

11101110
def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") {
1111-
val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree)
1111+
val TypeBoundsTree(lo, hi) = tree
11121112
val lo1 = typed(lo)
11131113
val hi1 = typed(hi)
1114-
val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1)
1114+
1115+
val lo2 = if (lo1.isEmpty) typed(untpd.TypeTree(hi1.typeOpt.bottomType)) else lo1
1116+
val hi2 = if (hi1.isEmpty) typed(untpd.TypeTree(lo1.typeOpt.topType)) else hi1
1117+
1118+
val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo2, hi2), lo2, hi2)
11151119
if (ctx.mode.is(Mode.Pattern)) {
11161120
// Associate a pattern-bound type symbol with the wildcard.
11171121
// The bounds of the type symbol can be constrained when comparing a pattern type
@@ -1330,6 +1334,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13301334
cls, isRequired, cdef.pos)
13311335
}
13321336

1337+
// Check that phantom lattices are defined in a static object
1338+
if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner)
1339+
ctx.error("only static objects can extend scala.Phantom", cdef.pos)
1340+
13331341
// check value class constraints
13341342
checkDerivedValueClass(cls, body1)
13351343

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class CompilationTests extends ParallelTesting {
152152
compileFile("../tests/neg/customArgs/noimports2.scala", defaultOptions.and("-Yno-imports")) +
153153
compileFile("../tests/neg/customArgs/overloadsOnAbstractTypes.scala", allowDoubleBindings) +
154154
compileFile("../tests/neg/customArgs/xfatalWarnings.scala", defaultOptions.and("-Xfatal-warnings")) +
155+
compileFile("../tests/neg/customArgs/phantom-overload.scala", allowDoubleBindings) +
155156
compileFile("../tests/neg/tailcall/t1672b.scala", defaultOptions) +
156157
compileFile("../tests/neg/tailcall/t3275.scala", defaultOptions) +
157158
compileFile("../tests/neg/tailcall/t6574.scala", defaultOptions) +

0 commit comments

Comments
 (0)