diff --git a/community-build/community-projects/scodec b/community-build/community-projects/scodec index fac1efba2bf9..df437ccb17bd 160000 --- a/community-build/community-projects/scodec +++ b/community-build/community-projects/scodec @@ -1 +1 @@ -Subproject commit fac1efba2bf9ea353f026bb0f7a3b825a691584a +Subproject commit df437ccb17bdfe768149bc81e784c0b9f8ab0ed8 diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 19f8cc586279..e23483f05b21 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -605,7 +605,7 @@ object desugar { cpy.ValDef(vparam)(rhs = copyDefault(vparam))) val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => cpy.ValDef(vparam)(rhs = EmptyTree)) - DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) + DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, classTypeRef, creatorExpr) .withMods(Modifiers(Synthetic | constr1.mods.flags & copiedAccessFlags, constr1.mods.privateWithin)) :: Nil } } @@ -656,15 +656,6 @@ object desugar { // For all other classes, the parent is AnyRef. val companions = if (isCaseClass) { - // The return type of the `apply` method, and an (empty or singleton) list - // of widening coercions - val (applyResultTpt, widenDefs) = - if (!isEnumCase) - (TypeTree(), Nil) - else if (parents.isEmpty || enumClass.typeParams.isEmpty) - (enumClassTypeRef, Nil) - else - enumApplyResult(cdef, parents, derivedEnumParams, appliedRef(enumClassRef, derivedEnumParams)) // true if access to the apply method has to be restricted // i.e. if the case class constructor is either private or qualified private @@ -695,8 +686,6 @@ object desugar { then anyRef else constrVparamss.foldRight(classTypeRef)((vparams, restpe) => Function(vparams map (_.tpt), restpe)) - def widenedCreatorExpr = - widenDefs.foldLeft(creatorExpr)((rhs, meth) => Apply(Ident(meth.name), rhs :: Nil)) val applyMeths = if (mods.is(Abstract)) Nil else { @@ -709,9 +698,8 @@ object desugar { val appParamss = derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) => ap.withMods(ap.mods | (cp.mods.flags & HasDefault))) - val app = DefDef(nme.apply, derivedTparams, appParamss, applyResultTpt, widenedCreatorExpr) - .withMods(appMods) - app :: widenDefs + DefDef(nme.apply, derivedTparams, appParamss, classTypeRef, creatorExpr) + .withMods(appMods) :: Nil } val unapplyMeth = { val hasRepeatedParam = constrVparamss.head.exists { @@ -720,7 +708,7 @@ object desugar { val methName = if (hasRepeatedParam) nme.unapplySeq else nme.unapply val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name) - val unapplyResTp = if (arity == 0) Literal(Constant(true)) else TypeTree() + val unapplyResTp = if arity == 0 then Literal(Constant(true)) else classTypeRef DefDef(methName, derivedTparams, (unapplyParam :: Nil) :: Nil, unapplyResTp, unapplyRHS) .withMods(synthetic) } diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 4ed857678b26..0ebcd76d3ed0 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -139,7 +139,7 @@ object DesugarEnums { CaseDef(Ident(nme.WILDCARD), EmptyTree, Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(msg :: Nil)))) val stringCases = enumValues.map(enumValue => - CaseDef(Literal(Constant(enumValue.name.toString)), EmptyTree, enumValue) + CaseDef(Literal(Constant(enumValue.name.toString)), EmptyTree, Typed(enumValue, rawEnumClassRef)) ) ::: defaultCase :: Nil Match(Ident(nme.nameDollar), stringCases) val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil), @@ -157,12 +157,13 @@ object DesugarEnums { def byOrdinal: List[Tree] = if isJavaEnum || !constraints.cached then Nil else + val rawEnumClassRef = rawRef(enumClass.typeRef) val defaultCase = val ord = Ident(nme.ordinal) val err = Throw(New(TypeTree(defn.IndexOutOfBoundsException.typeRef), List(Select(ord, nme.toString_) :: Nil))) CaseDef(ord, EmptyTree, err) val valueCases = constraints.enumCases.map((i, enumValue) => - CaseDef(Literal(Constant(i)), EmptyTree, enumValue) + CaseDef(Literal(Constant(i)), EmptyTree, Typed(enumValue, rawEnumClassRef)) ) ::: defaultCase :: Nil val fromOrdinalDef = DefDef(nme.fromOrdinalDollar, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil), rawRef(enumClass.typeRef), Match(Ident(nme.ordinalDollar_), valueCases)) diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index a392b0ddb10b..2c543e9c9ad7 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -346,6 +346,10 @@ trait ConstraintHandling { val tpw = tp.widenUnion if (tpw ne tp) && (tpw <:< bound) then tpw else tp + def widenEnum(tp: Type) = + val tpw = tp.widenEnumCase + if (tpw ne tp) && (tpw <:< bound) then tpw else tp + def widenSingle(tp: Type) = val tpw = tp.widenSingletons if (tpw ne tp) && (tpw <:< bound) then tpw else tp @@ -354,14 +358,19 @@ trait ConstraintHandling { case WildcardType(optBounds) => optBounds.exists && isSingleton(optBounds.bounds.hi) case _ => isSubTypeWhenFrozen(tp, defn.SingletonType) + def isEnum(tp: Type): Boolean = tp match + case WildcardType(optBounds) => optBounds.exists && isEnum(optBounds.bounds.hi) + case _ => tp.typeSymbol.is(Enum, butNot=JavaDefined) + val wideInst = if isSingleton(bound) then inst - else dropSuperTraits(widenOr(widenSingle(inst))) + else + val lub = widenOr(widenSingle(inst)) + val asAdt = if isEnum(bound) then lub else widenEnum(lub) + dropSuperTraits(asAdt) wideInst match - case wideInst: TypeRef if wideInst.symbol.is(Module) => - TermRef(wideInst.prefix, wideInst.symbol.sourceModule) - case _ => - wideInst.dropRepeatedAnnot + case wideInst @ ModuleOrEnumValueRef() => wideInst + case wideInst => wideInst.dropRepeatedAnnot end widenInferred /** The instance type of `param` in the current constraint (which contains `param`). diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 950963497fbc..edcf77970dca 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -161,4 +161,3 @@ object CyclicReference { ex } } - diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8e328c0c882a..1acba1ee7b22 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1099,10 +1099,22 @@ object Types { case _ => this } + /** same as widen, but preserves modules and singleton enum values */ + final def widenToModule(using Context): Type = + def widenSingletonToModule(self: Type)(using Context): Type = self.stripTypeVar.stripAnnots match + case tp @ ModuleOrEnumValueRef() => tp + case tp: SingletonType if !tp.isOverloaded => widenSingletonToModule(tp.underlying) + case _ => self + widenSingletonToModule(this) match + case tp: ExprType => tp.resultType.widenToModule + case tp => tp + /** Widen from TermRef to its underlying non-termref * base type, while also skipping Expr types. + * Preserves references to modules or singleton enum values */ final def widenTermRefExpr(using Context): Type = stripTypeVar match { + case tp @ ModuleOrEnumValueRef() => tp case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr case _ => this } @@ -1145,7 +1157,7 @@ object Types { * Exception (if `-YexplicitNulls` is set): if this type is a nullable union (i.e. of the form `T | Null`), * then the top-level union isn't widened. This is needed so that type inference can infer nullable types. */ - def widenUnion(using Context): Type = widen match { + def widenUnion(using Context): Type = widenToModule match { case tp @ OrNull(tp1): OrType => // Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions. val tp1Widen = tp1.widenUnionWithoutNull @@ -1155,11 +1167,11 @@ object Types { tp.widenUnionWithoutNull } - def widenUnionWithoutNull(using Context): Type = widen match { + def widenUnionWithoutNull(using Context): Type = widenToModule match { case tp @ OrType(lhs, rhs) => TypeComparer.lub(lhs.widenUnionWithoutNull, rhs.widenUnionWithoutNull, canConstrain = true) match { case union: OrType => union.join - case res => res + case res => res } case tp @ AndType(tp1, tp2) => tp derived_& (tp1.widenUnionWithoutNull, tp2.widenUnionWithoutNull) @@ -1173,13 +1185,19 @@ object Types { tp } + def widenEnumCase(using Context): Type = dealias match { + case tp: (TypeRef | AppliedType) if tp.typeSymbol.isAllOf(EnumCase) => tp.parents.head + case tp: TermRef if tp.termSymbol.isAllOf(EnumCase, butNot=JavaDefined) => tp.underlying.widenExpr + case _ => this + } + /** Widen all top-level singletons reachable by dealiasing * and going to the operands of & and |. * Overridden and cached in OrType. */ def widenSingletons(using Context): Type = dealias match { case tp: SingletonType => - tp.widen + tp.widenToModule case tp: OrType => val tp1w = tp.widenSingletons if (tp1w eq tp) this else tp1w @@ -2548,6 +2566,11 @@ object Types { apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot) } + object ModuleOrEnumValueRef: + def unapply(tp: TermRef)(using Context): Boolean = + val sym = tp.termSymbol + sym.isAllOf(EnumCase, butNot=JavaDefined) || sym.is(Module) + object TypeRef { /** Create a type ref with given prefix and name */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 241319ea6ff7..672d76c3fafb 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1399,8 +1399,8 @@ object Scanners { object IndentWidth { private inline val MaxCached = 40 - private val spaces = Array.tabulate(MaxCached + 1)(new Run(' ', _)) - private val tabs = Array.tabulate(MaxCached + 1)(new Run('\t', _)) + private val spaces = Array.tabulate[Run](MaxCached + 1)(new Run(' ', _)) // TODO: remove new after bootstrap + private val tabs = Array.tabulate[Run](MaxCached + 1)(new Run('\t', _)) // TODO: remove new after bootstrap def Run(ch: Char, n: Int): Run = if (n <= MaxCached && ch == ' ') spaces(n) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index ade70b71dd7c..1dbbd63bd735 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -141,7 +141,7 @@ class PlainPrinter(_ctx: Context) extends Printer { toTextRHS(tp) case tp: TermRef if !tp.denotationIsCurrent && !homogenizedView || // always print underlying when testing picklers - tp.symbol.is(Module) || tp.symbol.name == nme.IMPORT => + tp.symbol.is(Module) || tp.symbol.isAllOf(EnumCase) || tp.symbol.name == nme.IMPORT => toTextRef(tp) ~ ".type" case tp: TermRef if tp.denot.isOverloaded => "" @@ -598,4 +598,3 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def coloredText(text: Text, color: String): Text = if (ctx.useColors) color ~ text ~ SyntaxHighlighting.NoColor else text } - diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 5ce5832dfd06..f5243c22ab4e 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -166,9 +166,10 @@ class ReifyQuotes extends MacroTransform { val meth = if (isType) ref(defn.Unpickler_unpickleType).appliedToType(originalTp) else - val tpe = - if originalTp =:= defn.NilModule.termRef then originalTp // Workaround #4987 - else originalTp.widen.dealias + val tpe = originalTp.widenToModule.dealias + // val tpe = + // if originalTp =:= defn.NilModule.termRef then originalTp // Workaround #4987 + // else originalTp.widen.dealias ref(defn.Unpickler_unpickleExpr).appliedToType(tpe) val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType) val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.AnyType)) diff --git a/tests/patmat/i7186.scala b/tests/patmat/i7186.scala index 95c7d4db02c4..ea8229e4b73f 100644 --- a/tests/patmat/i7186.scala +++ b/tests/patmat/i7186.scala @@ -125,78 +125,78 @@ object printMips { case Syscall => s"${indent}syscall$endl" case jal: Jal => - oneAddr(jal,indent)(_.dest) + oneAddr(jal,indent)(_.enumLabel,_.dest) case jr: Jr => - oneAddr(jr,indent)(_.dest) + oneAddr(jr,indent)(_.enumLabel,_.dest) case j: J => - oneAddr(j,indent)(_.dest) + oneAddr(j,indent)(_.enumLabel,_.dest) case li: Li => - twoAddr(li,indent)(_.dest,_.source) + twoAddr(li,indent)(_.enumLabel,_.dest,_.source) case lw: Lw => - twoAddr(lw,indent)(_.dest,_.source) + twoAddr(lw,indent)(_.enumLabel,_.dest,_.source) case neg: Neg => - twoAddr(neg,indent)(_.dest,_.r) + twoAddr(neg,indent)(_.enumLabel,_.dest,_.r) case not: Not => - twoAddr(not,indent)(_.dest,_.r) + twoAddr(not,indent)(_.enumLabel,_.dest,_.r) case move: Move => - twoAddr(move,indent)(_.dest,_.source) + twoAddr(move,indent)(_.enumLabel,_.dest,_.source) case beqz: Beqz => - twoAddr(beqz,indent)(_.source,_.breakTo) + twoAddr(beqz,indent)(_.enumLabel,_.source,_.breakTo) case sw: Sw => - twoAddr(sw,indent)(_.source,_.dest) + twoAddr(sw,indent)(_.enumLabel,_.source,_.dest) case add: Add => - threeAddr(add,indent)(_.dest,_.l,_.r) + threeAddr(add,indent)(_.enumLabel,_.dest,_.l,_.r) case sub: Sub => - threeAddr(sub,indent)(_.dest,_.l,_.r) + threeAddr(sub,indent)(_.enumLabel,_.dest,_.l,_.r) case mul: Mul => - threeAddr(mul,indent)(_.dest,_.l,_.r) + threeAddr(mul,indent)(_.enumLabel,_.dest,_.l,_.r) case div: Div => - threeAddr(div,indent)(_.dest,_.l,_.r) + threeAddr(div,indent)(_.enumLabel,_.dest,_.l,_.r) case rem: Rem => - threeAddr(rem,indent)(_.dest,_.l,_.r) + threeAddr(rem,indent)(_.enumLabel,_.dest,_.l,_.r) case seq: Seq => - threeAddr(seq,indent)(_.dest,_.l,_.r) + threeAddr(seq,indent)(_.enumLabel,_.dest,_.l,_.r) case sne: Sne => - threeAddr(sne,indent)(_.dest,_.l,_.r) + threeAddr(sne,indent)(_.enumLabel,_.dest,_.l,_.r) case slt: Slt => - threeAddr(slt,indent)(_.dest,_.l,_.r) + threeAddr(slt,indent)(_.enumLabel,_.dest,_.l,_.r) case sgt: Sgt => - threeAddr(sgt,indent)(_.dest,_.l,_.r) + threeAddr(sgt,indent)(_.enumLabel,_.dest,_.l,_.r) case sle: Sle => - threeAddr(sle,indent)(_.dest,_.l,_.r) + threeAddr(sle,indent)(_.enumLabel,_.dest,_.l,_.r) case sge: Sge => - threeAddr(sge,indent)(_.dest,_.l,_.r) + threeAddr(sge,indent)(_.enumLabel,_.dest,_.l,_.r) case _ => s"${indent}???$endl" } } - def oneAddr[O] - ( a: O, indent: String) - ( r: O => Dest, - ): String = { - val name = a.getClass.getSimpleName.toLowerCase - s"${indent}$name ${rsrc(r(a))}$endl" - } - - def twoAddr[O] - ( a: O, indent: String) - ( d: O => Register, - r: O => Dest | Constant - ): String = { - val name = a.getClass.getSimpleName.toLowerCase - s"${indent}$name ${registers(d(a))}, ${rsrc(r(a))}$endl" - } - - def threeAddr[O] - ( a: O, + def oneAddr[T] + ( a: T, indent: String ) + ( c: a.type => String, + r: a.type => Dest, + ): String = ( + s"${indent}${c(a)} ${rsrc(r(a))}$endl" + ) + + def twoAddr[T] + ( a: T, indent: String ) + ( c: a.type => String, + d: a.type => Register, + r: a.type => Dest | Constant + ): String = ( + s"${indent}${c(a)} ${registers(d(a))}, ${rsrc(r(a))}$endl" + ) + + def threeAddr[T] + ( a: T, indent: String ) - ( d: O => Register, - l: O => Register, - r: O => Src - ): String = { - val name = a.getClass.getSimpleName.toLowerCase - s"${indent}$name ${registers(d(a))}, ${registers(l(a))}, ${rsrc(r(a))}$endl" - } + ( c: a.type => String, + d: a.type => Register, + l: a.type => Register, + r: a.type => Src + ): String = ( + s"${indent}${c(a)} ${registers(d(a))}, ${registers(l(a))}, ${rsrc(r(a))}$endl" + ) def rsrc(v: Constant | Dest): String = v match { case Constant(c) => c.toString diff --git a/tests/pos/i3935.scala b/tests/pos/i3935.scala new file mode 100644 index 000000000000..4a3af9e3b6ec --- /dev/null +++ b/tests/pos/i3935.scala @@ -0,0 +1,10 @@ +enum Foo3[T](x: T) { + case Bar[S, T](y: T) extends Foo3[y.type](y) +} + +val foo: Foo3.Bar[Nothing, 3] = Foo3.Bar(3) +val bar = foo + +def baz[T](f: Foo3[T]): f.type = f + +val qux = baz(bar) // existentials are back in Dotty? diff --git a/tests/pos/i6781.scala b/tests/pos/i6781.scala new file mode 100644 index 000000000000..c6ebd9c9c36f --- /dev/null +++ b/tests/pos/i6781.scala @@ -0,0 +1,12 @@ +enum Nat { + case Zero + case Succ[N <: Nat](n: N) +} +import Nat._ + +inline def toInt(n: => Nat): Int = inline n match { + case Zero => 0 + case Succ(n1) => toInt(n1) + 1 +} + +val natTwo = toInt(Succ(Succ(Zero))) diff --git a/tests/pos/largeEnums.scala b/tests/pos/largeEnums.scala new file mode 100644 index 000000000000..524c33351725 --- /dev/null +++ b/tests/pos/largeEnums.scala @@ -0,0 +1,58 @@ +enum MySuperLongJavaEnum extends java.lang.Enum[MySuperLongJavaEnum] { + case + A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0, + A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1, + A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2, + A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, L3, M3, N3, O3, P3, Q3, R3, S3, T3, U3, V3, W3, X3, Y3, Z3, + A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, L4, M4, N4, O4, P4, Q4, R4, S4, T4, U4, V4, W4, X4, Y4, Z4, + A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, L5, M5, N5, O5, P5, Q5, R5, S5, T5, U5, V5, W5, X5, Y5, Z5 +} + +enum MySuperLongScalaEnum { + case + A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0, + A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1, + A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2, + A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, L3, M3, N3, O3, P3, Q3, R3, S3, T3, U3, V3, W3, X3, Y3, Z3, + A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, L4, M4, N4, O4, P4, Q4, R4, S4, T4, U4, V4, W4, X4, Y4, Z4, + A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, L5, M5, N5, O5, P5, Q5, R5, S5, T5, U5, V5, W5, X5, Y5, Z5 +} + +trait FooBar + +enum MySuperLongAdt { + case A0 extends MySuperLongAdt with FooBar + case B0 extends MySuperLongAdt with FooBar + case C0 extends MySuperLongAdt with FooBar + case D0 extends MySuperLongAdt with FooBar + case E0 extends MySuperLongAdt with FooBar + case F0 extends MySuperLongAdt with FooBar + case G0 extends MySuperLongAdt with FooBar + case H0 extends MySuperLongAdt with FooBar + case I0 extends MySuperLongAdt with FooBar + case J0 extends MySuperLongAdt with FooBar + case K0 extends MySuperLongAdt with FooBar + case L0 extends MySuperLongAdt with FooBar + case M0 extends MySuperLongAdt with FooBar + case N0 extends MySuperLongAdt with FooBar + case O0 extends MySuperLongAdt with FooBar + case P0 extends MySuperLongAdt with FooBar + case Q0 extends MySuperLongAdt with FooBar + case R0 extends MySuperLongAdt with FooBar + case S0 extends MySuperLongAdt with FooBar + case T0 extends MySuperLongAdt with FooBar + case U0 extends MySuperLongAdt with FooBar + case V0 extends MySuperLongAdt with FooBar + case Acc[T <: MySuperLongAdt](t: T) +} + +object Test { + + import MySuperLongAdt._ + val sum: ( + A0.type | B0.type | C0.type | D0.type | E0.type | F0.type | G0.type | H0.type | I0.type | J0.type | K0.type | + L0.type | M0.type | N0.type | O0.type | P0.type | Q0.type | R0.type | S0.type | T0.type | U0.type | V0.type + ) = R0 + + def test = Acc(sum) +} diff --git a/tests/run-macros/enum-nat-macro.scala/Macros_2.scala b/tests/run-macros/enum-nat-macro.scala/Macros_2.scala new file mode 100644 index 000000000000..7e9f0e14b042 --- /dev/null +++ b/tests/run-macros/enum-nat-macro.scala/Macros_2.scala @@ -0,0 +1,34 @@ +import Nat._ + +inline def toIntMacro(inline nat: Nat): Int = ${ Macros.toIntImpl('nat) } +inline def ZeroMacro: Zero.type = ${ Macros.natZero } +transparent inline def toNatMacro(inline int: Int): Nat = ${ Macros.toNatImpl('int) } + +inline def toIntInline(inline nat: Nat): Int = inline nat match + case Zero => 0 + case Succ(n) => toIntInline(n) + 1 + +object Macros: + import quoted._ + + def toIntImpl(nat: Expr[Nat])(using QuoteContext): Expr[Int] = + + def inner(nat: Expr[Nat], acc: Int): Int = nat match + case '{ Succ($nat) } => inner(nat, acc + 1) + case '{ Zero } => acc + + Expr(inner(nat, 0)) + + def natZero(using QuoteContext): Expr[Nat.Zero.type] = '{Zero} + + def toNatImpl(int: Expr[Int])(using QuoteContext): Expr[Nat] = + + // it seems even with the bound that the arg will always widen to Expr[Nat] unless explicit + + def inner[N <: Nat: Type](int: Int, acc: Expr[N]): Expr[Nat] = int match + case 0 => acc + case n => inner[Succ[N]](n - 1, '{Succ($acc)}) + + val Const(i) = int + require(i >= 0) + inner[Zero.type](i, '{Zero}) diff --git a/tests/run-macros/enum-nat-macro.scala/Nat_1.scala b/tests/run-macros/enum-nat-macro.scala/Nat_1.scala new file mode 100644 index 000000000000..fbb209698469 --- /dev/null +++ b/tests/run-macros/enum-nat-macro.scala/Nat_1.scala @@ -0,0 +1,3 @@ +enum Nat: + case Zero + case Succ[N <: Nat](n: N) diff --git a/tests/run-macros/enum-nat-macro.scala/Test_3.scala b/tests/run-macros/enum-nat-macro.scala/Test_3.scala new file mode 100644 index 000000000000..6e53f47d5d85 --- /dev/null +++ b/tests/run-macros/enum-nat-macro.scala/Test_3.scala @@ -0,0 +1,10 @@ +import Nat._ + +@main def Test: Unit = + assert(toIntMacro(Succ(Succ(Succ(Zero)))) == 3) + assert(toNatMacro(3) == Succ(Succ(Succ(Zero)))) + val zero: Zero.type = ZeroMacro + assert(zero == Zero) + assert(toIntMacro(toNatMacro(3)) == 3) + val n: Succ[Succ[Succ[Zero.type]]] = toNatMacro(3) + assert(toIntInline(n) == 3) // TODO: there is an error with positions when passing the result of toNatMacro(3) directly diff --git a/tests/run-macros/i8007.check b/tests/run-macros/i8007.check index 0ccbe496ef31..01586017aee7 100644 --- a/tests/run-macros/i8007.check +++ b/tests/run-macros/i8007.check @@ -11,5 +11,9 @@ true true +true + +true + false diff --git a/tests/run-macros/i8007/Macro_3.scala b/tests/run-macros/i8007/Macro_3.scala index a6e54a2bfc03..338f651f0dd5 100644 --- a/tests/run-macros/i8007/Macro_3.scala +++ b/tests/run-macros/i8007/Macro_3.scala @@ -64,7 +64,6 @@ object Eq { $ordx == $ordy && $elements($ordx).asInstanceOf[Eq[Any]].eqv($x, $y) } } - '{ eqSum((x: T, y: T) => ${eqSumBody('x, 'y)}) } @@ -76,4 +75,4 @@ object Macro3 { extension [T](x: =>T) inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] } -} \ No newline at end of file +} diff --git a/tests/run-macros/i8007/Test_4.scala b/tests/run-macros/i8007/Test_4.scala index f9cc926c4819..25f54bf2eb79 100644 --- a/tests/run-macros/i8007/Test_4.scala +++ b/tests/run-macros/i8007/Test_4.scala @@ -6,10 +6,22 @@ import Macro3.eqGen case class Person(name: String, age: Int) enum Opt[+T] { - case Sm(t: T) + case Sm[U](t: U) extends Opt[U] case Nn } +enum OptInfer[+T] { + case Sm[+U](t: U) extends OptInfer[U] + case Nn +} + +// simulation of Opt using case class hierarchy +sealed abstract class OptCase[+T] +object OptCase { + final case class Sm[T](t: T) extends OptCase[T] + case object Nn extends OptCase[Nothing] +} + @main def Test() = { import Opt._ import Eq.{given _, _} @@ -30,15 +42,23 @@ enum Opt[+T] { println(t4) // false println - val t5 = Sm(23) === Sm(23) + val t5 = Opt.Sm[Int](23) === Opt.Sm(23) // same behaviour as case class when using apply println(t5) // true println - val t6 = Sm(Person("Test", 23)) === Sm(Person("Test", 23)) + val t5_2 = OptCase.Sm[Int](23) === OptCase.Sm(23) + println(t5_2) // true + println + + val t5_3 = OptInfer.Sm(23) === OptInfer.Sm(23) // covariant `Sm` case means we can avoid explicit type parameter + println(t5_3) // true + println + + val t6 = Sm[Person](Person("Test", 23)) === Sm(Person("Test", 23)) println(t6) // true println - val t7 = Sm(Person("Test", 23)) === Sm(Person("Test", 24)) + val t7 = Sm[Person](Person("Test", 23)) === Sm(Person("Test", 24)) println(t7) // false println -} \ No newline at end of file +} diff --git a/tests/run/enum-nat.scala b/tests/run/enum-nat.scala new file mode 100644 index 000000000000..870688b76d5d --- /dev/null +++ b/tests/run/enum-nat.scala @@ -0,0 +1,34 @@ +import Nat._ +import compiletime._ + +enum Nat: + case Zero + case Succ[N <: Nat](n: N) + +inline def toIntTypeLevel[N <: Nat]: Int = inline erasedValue[N] match + case _: Zero.type => 0 + case _: Succ[n] => toIntTypeLevel[n] + 1 + +inline def toInt(inline nat: Nat): Int = inline nat match + case nat: Zero.type => 0 + case nat: Succ[n] => toInt(nat.n) + 1 + +inline def toIntUnapply(inline nat: Nat): Int = inline nat match + case Zero => 0 + case Succ(n) => toIntUnapply(n) + 1 + +inline def toIntTypeTailRec[N <: Nat, Acc <: Int]: Int = inline erasedValue[N] match + case _: Zero.type => constValue[Acc] + case _: Succ[n] => toIntTypeTailRec[n, S[Acc]] + +inline def toIntErased[N <: Nat](inline nat: N): Int = toIntTypeTailRec[N, 0] + +@main def Test: Unit = + println("erased value:") + assert(toIntTypeLevel[Succ[Succ[Succ[Zero.type]]]] == 3) + println("type test:") + assert(toInt(Succ(Succ(Succ(Zero)))) == 3) + println("unapply:") + assert(toIntUnapply(Succ(Succ(Succ(Zero)))) == 3) + println("infer erased:") + assert(toIntErased(Succ(Succ(Succ(Zero)))) == 3) diff --git a/tests/run/enum-precise.scala b/tests/run/enum-precise.scala new file mode 100644 index 000000000000..1ae98ca6664f --- /dev/null +++ b/tests/run/enum-precise.scala @@ -0,0 +1,31 @@ +enum NonEmptyList[+T]: + case Many[+U](head: U, tail: NonEmptyList[U]) extends NonEmptyList[U] + case One [+U](value: U) extends NonEmptyList[U] + +enum Ast: + case Binding(name: String, tpe: String) + case Lambda(args: NonEmptyList[Binding], rhs: Ast) // reference to another case of the enum + case Ident(name: String) + case Apply(fn: Ast, args: NonEmptyList[Ast]) + +import NonEmptyList._ +import Ast._ + +// This example showcases the widening when inferring enum case types. +// With scala 2 case class hierarchies, if One.apply(1) returns One[Int] and Many.apply(2, One(3)) returns Many[Int] +// then the `foldRight` expression below would complain that Many[Binding] is not One[Binding]. With Scala 3 enums, +// .apply on the companion returns the precise class, but type inference will widen to NonEmptyList[Binding] unless +// the precise class is expected. +def Bindings(arg: (String, String), args: (String, String)*): NonEmptyList[Binding] = + def Bind(arg: (String, String)): Binding = + val (name, tpe) = arg + Binding(name, tpe) + + args.foldRight(One[Binding](Bind(arg)))((arg, acc) => Many(Bind(arg), acc)) + +@main def Test: Unit = + val OneOfOne: One[1] = One[1](1) + val True = Lambda(Bindings("x" -> "T", "y" -> "T"), Ident("x")) + val Const = Lambda(One(Binding("x", "T")), Lambda(One(Binding("y", "U")), Ident("x"))) // precise type is forwarded + + assert(OneOfOne.value == 1)