From e90d3766538d47a87b18880c4a8bc0555d913293 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 23 Oct 2023 17:59:00 +0200 Subject: [PATCH 1/2] Avoid crash arising from trying to find conversions from polymorphic singleton types This is an alternative fix for #18695, which already got fixed in a different way by #18719. This PR adds the actual tests, and leaves in the fix as a defensive measure in case the situation arises by some other means than the one foxed in #18719. [Cherry-picked f995a8c5eeeaff97c682d802be81ab728387ce6a] --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 14 ++++++-------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 8 +++++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 72e9e58eefad..a0913b628abd 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -45,21 +45,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Apply(fn: Tree, args: List[Tree])(using Context): Apply = fn match case Block(Nil, expr) => Apply(expr, args) + case _: RefTree | _: GenericApply | _: Inlined | _: Hole => + ta.assignType(untpd.Apply(fn, args), fn, args) case _ => - assert( - fn.isInstanceOf[RefTree | GenericApply | Inlined | Hole] || ctx.reporter.errorsReported, - s"Illegal Apply function prefix: $fn" - ) + assert(ctx.reporter.errorsReported) ta.assignType(untpd.Apply(fn, args), fn, args) def TypeApply(fn: Tree, args: List[Tree])(using Context): TypeApply = fn match case Block(Nil, expr) => TypeApply(expr, args) + case _: RefTree | _: GenericApply => + ta.assignType(untpd.TypeApply(fn, args), fn, args) case _ => - assert( - fn.isInstanceOf[RefTree | GenericApply] || ctx.reporter.errorsReported, - s"Illegal TypeApply function prefix: $fn" - ) + assert(ctx.reporter.errorsReported, s"unexpected tree for type application: $fn") ta.assignType(untpd.TypeApply(fn, args), fn, args) def Literal(const: Constant)(using Context): Literal = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 65b474f24b3e..bf6e0631ca10 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4267,7 +4267,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => adaptOverloaded(ref) } - case poly: PolyType if !(ctx.mode is Mode.Type) => + case poly: PolyType + if !(ctx.mode is Mode.Type) && dummyTreeOfType.unapply(tree).isEmpty => + // If we are in a conversion from a TermRef with polymorphic underlying + // type, give up. In this case the typed `null` literal cannot be instantiated. + // Test case was but i18695.scala, but it got fixed by a different tweak in #18719. + // We leave test for this condition in as a defensive measure in case + // it arises somewhere else. if isApplyProxy(tree) then newExpr else if pt.isInstanceOf[PolyProto] then tree else From a27caa4f4fc172f60d99c4fafe7cfaf5e9fb875d Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 25 Oct 2023 17:35:10 +0200 Subject: [PATCH 2/2] Add test cases [Cherry-picked 125612f44c4c4794c0037ea080553dc0210be597] --- tests/neg-macros/i18695.scala | 75 +++++++++++++++++++++++++++++++++++ tests/neg/i18695.scala | 3 ++ 2 files changed, 78 insertions(+) create mode 100644 tests/neg-macros/i18695.scala create mode 100644 tests/neg/i18695.scala diff --git a/tests/neg-macros/i18695.scala b/tests/neg-macros/i18695.scala new file mode 100644 index 000000000000..7427839cdbe3 --- /dev/null +++ b/tests/neg-macros/i18695.scala @@ -0,0 +1,75 @@ +import scala.annotation.{tailrec, unused} +import scala.deriving.Mirror +import scala.quoted.* + +trait TypeLength[A] { + type Length <: Int + def length: Length +} +object TypeLength extends TypeLengthLowPriority: + type Aux[A, Length0 <: Int] = TypeLength[A] { + type Length = Length0 + } + + transparent inline given fromMirror[A](using m: Mirror.Of[A]): TypeLength[A] = + ${ macroImpl[A, m.MirroredElemTypes] } + + @tailrec + private def typesOfTuple( + using q: Quotes + )(tpe: q.reflect.TypeRepr, acc: List[q.reflect.TypeRepr]): List[q.reflect.TypeRepr] = + import q.reflect.* + val cons = Symbol.classSymbol("scala.*:") + tpe.widenTermRefByName.dealias match + case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) => + tpes.reverse_:::(acc) + case AppliedType(tp, List(headType, tailType)) if tp.derivesFrom(cons) => + typesOfTuple(tailType, headType :: acc) + case tpe => + if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then acc.reverse + else report.errorAndAbort(s"Unknown type encountered in tuple ${tpe.show}") + + def macroImpl[A: Type, T <: Tuple: scala.quoted.Type]( + using q: scala.quoted.Quotes + ): scala.quoted.Expr[TypeLength[A]] = + import q.reflect.* + val l = typesOfTuple(TypeRepr.of[T], Nil).length + ConstantType(IntConstant(l)).asType match + case '[lt] => + val le = Expr[Int](l).asExprOf[lt & Int] + '{ + val r: TypeLength.Aux[A, lt & Int] = new TypeLength[A] { + type Length = lt & Int + val length: Length = ${ le } + } + r + } + + transparent inline given fromTuple[T <: Tuple]: TypeLength[T] = + ${ macroImpl[T, T] } + +trait TypeLengthLowPriority: + self: TypeLength.type => + given tupleFromMirrorAndLength[A, T <: Tuple]( + using @unused m: Mirror.Of[A] { type MirroredElemTypes = T }, + length: TypeLength[A] + ): TypeLength.Aux[T, length.Length] = length.asInstanceOf[TypeLength.Aux[T, length.Length]] + +trait HKDSumGeneric[A] +object HKDSumGeneric: + type NotZero[N <: Int] = N match + case 0 => false + case _ => true + + transparent inline given derived[A](using m: Mirror.SumOf[A], typeLength: TypeLength[A])( + using NotZero[typeLength.Length] =:= true + ): HKDSumGeneric[A] = + derivedImpl[A, m.MirroredElemTypes, m.MirroredLabel] // error + + def derivedImpl[A, ElemTypes <: Tuple, Label <: String]( + using m: Mirror.SumOf[A] { + type MirroredElemTypes = ElemTypes; type MirroredLabel = Label; + }, + typeLength: TypeLength[ElemTypes], + nz: NotZero[typeLength.Length] =:= true + ): HKDSumGeneric[A] = ??? \ No newline at end of file diff --git a/tests/neg/i18695.scala b/tests/neg/i18695.scala new file mode 100644 index 000000000000..e10f30b26779 --- /dev/null +++ b/tests/neg/i18695.scala @@ -0,0 +1,3 @@ +trait Foo { type Num <: Int } +given derived[A](using foo: Foo): Any = derivedImpl(foo) // error +def derivedImpl(foo: Foo)(using bar: foo.Num =:= Int): Any = ???