Skip to content

Commit

Permalink
Fixes for isLegalPrefix change (#22241)
Browse files Browse the repository at this point in the history
Fixes #22062
Fixes #22068
Fixes #22070
  • Loading branch information
odersky authored Jan 13, 2025
2 parents 74aa123 + 08fef87 commit a5a9fc8
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 19 deletions.
17 changes: 16 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,22 @@ object TypeOps:
tp
else (if pre.isSingleton then NoType else tryWiden(tp, tp.prefix)).orElse {
if (tp.isTerm && variance > 0 && !pre.isSingleton)
apply(tp.info.widenExpr)
tp.prefix match
case inlines.Inliner.OpaqueProxy(ref) =>
// Strip refinements on an opaque alias proxy
// Using pos/i22068 as an example,
// Inliner#addOpaqueProxies add the following opaque alias proxy:
// val $proxy1: foos.type { type Foo[T] = String } =
// foos.$asInstanceOf[foos.type { type Foo[T] = String }]
// Then when InlineCall#expand creates a typed Inlined,
// we type avoid any local bindings, which includes that opaque alias proxy.
// To avoid that the replacement is a non-singleton RefinedType,
// we drop the refinements too and return foos.type.
// That way, when we inline `def m1` and we calculate the asSeenFrom
// of `b1.and(..)` b1 doesn't have an unstable prefix.
derivedSelect(tp, ref)
case _ =>
apply(tp.info.widenExpr)
else if (upper(pre).member(tp.name).exists)
super.derivedSelect(tp, pre)
else
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,10 @@ class TreeUnpickler(reader: TastyReader,
case TYPEREFin =>
val name = readName().toTypeName
val prefix = readType()
def pre = if TypeOps.isLegalPrefix(prefix) then prefix else QualSkolemType(prefix)
val space = readType()
space.decl(name) match {
case symd: SymDenotation if prefix.isArgPrefixOf(symd.symbol) => TypeRef(prefix, symd.symbol)
case symd: SymDenotation if prefix.isArgPrefixOf(symd.symbol) => TypeRef(pre, symd.symbol)
case _ => TypeRef(prefix, name, space.decl(name).asSeenFrom(prefix))
}
case REFINEDtype =>
Expand Down
53 changes: 38 additions & 15 deletions compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,39 @@ object Inliner:
case _ => tree
else super.transformInlined(tree)
end InlinerMap

object OpaqueProxy:

def apply(ref: TermRef, cls: ClassSymbol, span: Span)(using Context): TermRef =
def openOpaqueAliases(selfType: Type): List[(Name, Type)] = selfType match
case RefinedType(parent, rname, TypeAlias(alias)) =>
val opaq = cls.info.member(rname).symbol
if opaq.isOpaqueAlias then
(rname, alias.stripLazyRef.asSeenFrom(ref, cls))
:: openOpaqueAliases(parent)
else Nil
case _ => Nil
val refinements = openOpaqueAliases(cls.givenSelfType)
val refinedType = refinements.foldLeft(ref: Type): (parent, refinement) =>
RefinedType(parent, refinement._1, TypeAlias(refinement._2))
val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, refinedType, span)
refiningSym.termRef

def unapply(refiningRef: TermRef)(using Context): Option[TermRef] =
val refiningSym = refiningRef.symbol
if refiningSym.name.is(InlineBinderName) && refiningSym.is(Synthetic, butNot=InlineProxy) then
refiningRef.info match
case refinedType: RefinedType => refinedType.stripRefinement match
case ref: TermRef => Some(ref)
case _ => None
case _ => None
else
None

end OpaqueProxy

private[inlines] def newSym(name: Name, flags: FlagSet, info: Type, span: Span)(using Context): Symbol =
newSymbol(ctx.owner, name, flags, info, coord = span)
end Inliner

/** Produces an inlined version of `call` via its `inlined` method.
Expand Down Expand Up @@ -189,7 +222,7 @@ class Inliner(val call: tpd.Tree)(using Context):
private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]

private[inlines] def newSym(name: Name, flags: FlagSet, info: Type)(using Context): Symbol =
newSymbol(ctx.owner, name, flags, info, coord = call.span)
Inliner.newSym(name, flags, info, call.span)

/** A binding for the parameter of an inline method. This is a `val` def for
* by-value parameters and a `def` def for by-name parameters. `val` defs inherit
Expand Down Expand Up @@ -351,20 +384,9 @@ class Inliner(val call: tpd.Tree)(using Context):
&& (forThisProxy || inlinedMethod.isContainedIn(cls))
&& mapRef(ref).isEmpty
then
def openOpaqueAliases(selfType: Type): List[(Name, Type)] = selfType match
case RefinedType(parent, rname, TypeAlias(alias)) =>
val opaq = cls.info.member(rname).symbol
if opaq.isOpaqueAlias then
(rname, alias.stripLazyRef.asSeenFrom(ref, cls))
:: openOpaqueAliases(parent)
else Nil
case _ =>
Nil
val refinements = openOpaqueAliases(cls.givenSelfType)
val refinedType = refinements.foldLeft(ref: Type) ((parent, refinement) =>
RefinedType(parent, refinement._1, TypeAlias(refinement._2))
)
val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, refinedType).asTerm
val refiningRef = OpaqueProxy(ref, cls, call.span)
val refiningSym = refiningRef.symbol.asTerm
val refinedType = refiningRef.info
val refiningDef = ValDef(refiningSym, tpd.ref(ref).cast(refinedType), inferred = true).withSpan(span)
inlining.println(i"add opaque alias proxy $refiningDef for $ref in $tp")
bindingsBuf += refiningDef
Expand Down Expand Up @@ -768,6 +790,7 @@ class Inliner(val call: tpd.Tree)(using Context):
override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
val locked = ctx.typerState.ownedVars
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
selectionType(tree, qual1) // side-effect
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
val reducedProjection = reducer.reduceProjection(resNoReduce)
if reducedProjection.isType then
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ object ResolveSuper {
sym = other.symbol
// Having a matching denotation is not enough: it should also be a subtype
// of the superaccessor's type, see i5433.scala for an example where this matters
val otherTp = other.asSeenFrom(base.typeRef).info
val accTp = acc.asSeenFrom(base.typeRef).info
val otherTp = other.asSeenFrom(base.thisType).info
val accTp = acc.asSeenFrom(base.thisType).info
// Since the super class can be Java defined,
// we use relaxed overriding check for explicit nulls if one of the symbols is Java defined.
// This forces `Null` to be a subtype of non-primitive value types during override checking.
Expand Down
32 changes: 32 additions & 0 deletions tests/pos/i22062.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class Label
class Component

trait RenderableCellsCompanion:
type Renderer[-A] <: CellRenderer[A]
type DefaultRenderer[-A] <: Label & Renderer[A]

trait CellRendererCompanion:
type CellInfo
def labeled[A](): DefaultRenderer[A]
protected trait LabelRenderer[-A] extends CellRenderer[A]:
override abstract def componentFor(info: companion.CellInfo): Component = super.componentFor(info)

trait CellRenderer[-A]:
val companion: CellRendererCompanion
def componentFor(cellInfo: companion.CellInfo): Component

sealed trait TreeRenderers extends RenderableCellsCompanion:
this: Tree.type =>

trait Renderer[-A] extends CellRenderer[A]:
final override val companion = Renderer

object Renderer extends CellRendererCompanion:
final override class CellInfo
override def labeled[A]() = new DefaultRenderer[A] with LabelRenderer[A] {}

class DefaultRenderer[-A] extends Label with Renderer[A]:
override def componentFor(info: Renderer.CellInfo): Component = ???

class Tree extends Component
object Tree extends TreeRenderers
21 changes: 21 additions & 0 deletions tests/pos/i22068.less-min.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class A
class B extends A
class C extends A

object foos:
opaque type Tag[A] = String
object Tag:
inline given mkTag[A]: Tag[A] = ???
type Full[A] = Tag[A] | Set[A]
sealed trait Set[A] extends Any
case class Union[A](tags: Seq[Tag[Any]]) extends AnyVal with Set[A]:
infix def and[B](t2: Full[B]): Unit = ???
object Union:
inline given mkUnion[A]: Union[A] = ???
import foos.Tag.*

class Test:
inline def m1[K1, K2](using b1: Union[K1], b2: Union[K2]): Unit =
b1.and(b2)

def t1(): Unit = m1[B | C, A]
29 changes: 29 additions & 0 deletions tests/pos/i22068.orig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
trait AnyFreeSpecLike:
inline implicit def convertToFreeSpecStringWrapper(s: String): FreeSpecStringWrapper = ???
protected final class FreeSpecStringWrapper(string: String):
infix def in(testFun: => Any): Unit = ???


import types.Tag.*
class TagTest extends AnyFreeSpecLike{
inline def test[T1, T2](using k1: Union[T1], k2: Union[T2]): Unit =
"T1 <:< T2" in {
val kresult = k1 <:< k2
???
}
class A
class B extends A
class C extends A
test[B | C, A]
}

object types:
opaque type Tag[A] = String
object Tag:
inline given apply[A]: Tag[A] = ???
type Full[A] = Tag[A] | Set[A]
sealed trait Set[A] extends Any
case class Union[A](tags: Seq[Tag[Any]]) extends AnyVal with Set[A]:
infix def <:<[B](t2: Full[B]): Boolean = ???
object Union:
inline given apply[A]: Union[A] = ???
14 changes: 14 additions & 0 deletions tests/pos/i22068.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object foos:
opaque type Foo[T] = String
object bars:
class Bar1[A] { def and(b: Bar2): Unit = () }
class Bar2
inline def mkBar1[A]: Bar1[A] = new Bar1[A]
def mkBar2 : Bar2 = new Bar2
import foos.*, bars.*

class Test:
inline def m1[X](b1: Bar1[X], b2: Bar2): Unit =
b1.and(b2)

def t1(): Unit = m1(mkBar1[Int], mkBar2)
18 changes: 18 additions & 0 deletions tests/pos/i22070/macro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
trait Featureful[T]:
def toFeatures(value: T): IArray[Float]

object Featureful:
inline def derived[T](using scala.deriving.Mirror.Of[T]) = ${ derivedImpl[T] }

import scala.quoted.*
private def derivedImpl[T: Type](using Quotes): Expr[Featureful[T]] =
import quotes.reflect.*
'{
new Featureful[T]:
def toFeatures(value: T) =
val feats = IArray.empty[Featureful[?]]
val product = value.asInstanceOf[Product]
product.productIterator.zipWithIndex.foreach: (any, idx) =>
feats(idx).toFeatures(any.asInstanceOf)
IArray.empty
}
1 change: 1 addition & 0 deletions tests/pos/i22070/usage.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
case class Breaks(x: Boolean, y: Boolean) derives Featureful

0 comments on commit a5a9fc8

Please sign in to comment.