Skip to content

Commit

Permalink
Avoid crash arising from trying to find conversions from polymorphic …
Browse files Browse the repository at this point in the history
…singleton types (#18760)
  • Loading branch information
dwijnand authored Oct 25, 2023
2 parents f98a3aa + 125612f commit 2cf4ac3
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 9 deletions.
14 changes: 6 additions & 8 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4366,7 +4366,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
Expand Down
75 changes: 75 additions & 0 deletions tests/neg-macros/i18695.scala
Original file line number Diff line number Diff line change
@@ -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] = ???
3 changes: 3 additions & 0 deletions tests/neg/i18695.scala
Original file line number Diff line number Diff line change
@@ -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 = ???

0 comments on commit 2cf4ac3

Please sign in to comment.