Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for isLegalPrefix change #22241

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading