diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 94862e282b69..71efc27bf673 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -28,13 +28,25 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): private type SpecialHandlers = List[(ClassSymbol, SpecialHandler)] val synthesizedClassTag: SpecialHandler = (formal, span) => + def instArg(tp: Type): Type = tp.stripTypeVar match + // Special case to avoid instantiating `Int & S` to `Int & Nothing` in + // i16328.scala. The intersection comes from an earlier instantiation + // to an upper bound. + // The dual situation with unions is harder to trigger because lower + // bounds are usually widened during instantiation. + case tp: AndOrType if tp.tp1 =:= tp.tp2 => + instArg(tp.tp1) + case _ => + if isFullyDefined(tp, ForceDegree.all) then tp + else NoType // this happens in tests/neg/i15372.scala + val tag = formal.argInfos match - case arg :: Nil if isFullyDefined(arg, ForceDegree.all) => - arg match + case arg :: Nil => + instArg(arg) match case defn.ArrayOf(elemTp) => val etag = typer.inferImplicitArg(defn.ClassTagClass.typeRef.appliedTo(elemTp), span) if etag.tpe.isError then EmptyTree else etag.select(nme.wrap) - case tp if hasStableErasure(tp) && !defn.isBottomClassAfterErasure(tp.typeSymbol) => + case tp if hasStableErasure(tp) && !tp.isBottomTypeAfterErasure => val sym = tp.typeSymbol val classTagModul = ref(defn.ClassTagModule) if defn.SpecialClassTagClasses.contains(sym) then diff --git a/tests/neg/i1730.scala b/tests/neg/i1730.scala new file mode 100644 index 000000000000..d88d3c007002 --- /dev/null +++ b/tests/neg/i1730.scala @@ -0,0 +1,7 @@ +import scala.reflect.ClassTag + +@main def Test = + val x: Array[? <: String] = Array[Int & Nothing]() // error: No ClassTag available for Int & Nothing + // (was: ClassCastException: [I cannot be cast to [Ljava.lang.String) + val y: Array[? <: Int] = Array[String & Nothing]() // error: No ClassTag available for String & Nothing + // (was: ClassCastException: [Lscala.runtime.Nothing$; cannot be cast to [I) diff --git a/tests/pos/i16328.scala b/tests/pos/i16328.scala new file mode 100644 index 000000000000..fd5b7e14d381 --- /dev/null +++ b/tests/pos/i16328.scala @@ -0,0 +1,19 @@ +import scala.reflect.ClassTag + +object Test { + def getParamType[T: ClassTag](x: T => Int): T = ??? + + def id[S](x: S): S = x + + def main(args: Array[String]) = { + // worked before + val a1 = getParamType((x: Int) => x) + val a2: Int = a1 // ensure that we actually got a ClassTag for the right type + + // broken before + val b1 = id(getParamType((x: Int) => x)) // was error + val b2: Int = b1 + val c1 = id(id(getParamType((x: Int) => x))) // was error + val c2: Int = c1 + } +}