Skip to content

Commit a5a9fc8

Browse files
authored
Fixes for isLegalPrefix change (#22241)
Fixes #22062 Fixes #22068 Fixes #22070
2 parents 74aa123 + 08fef87 commit a5a9fc8

File tree

10 files changed

+173
-19
lines changed

10 files changed

+173
-19
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

+16-1
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,22 @@ object TypeOps:
511511
tp
512512
else (if pre.isSingleton then NoType else tryWiden(tp, tp.prefix)).orElse {
513513
if (tp.isTerm && variance > 0 && !pre.isSingleton)
514-
apply(tp.info.widenExpr)
514+
tp.prefix match
515+
case inlines.Inliner.OpaqueProxy(ref) =>
516+
// Strip refinements on an opaque alias proxy
517+
// Using pos/i22068 as an example,
518+
// Inliner#addOpaqueProxies add the following opaque alias proxy:
519+
// val $proxy1: foos.type { type Foo[T] = String } =
520+
// foos.$asInstanceOf[foos.type { type Foo[T] = String }]
521+
// Then when InlineCall#expand creates a typed Inlined,
522+
// we type avoid any local bindings, which includes that opaque alias proxy.
523+
// To avoid that the replacement is a non-singleton RefinedType,
524+
// we drop the refinements too and return foos.type.
525+
// That way, when we inline `def m1` and we calculate the asSeenFrom
526+
// of `b1.and(..)` b1 doesn't have an unstable prefix.
527+
derivedSelect(tp, ref)
528+
case _ =>
529+
apply(tp.info.widenExpr)
515530
else if (upper(pre).member(tp.name).exists)
516531
super.derivedSelect(tp, pre)
517532
else

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,10 @@ class TreeUnpickler(reader: TastyReader,
395395
case TYPEREFin =>
396396
val name = readName().toTypeName
397397
val prefix = readType()
398+
def pre = if TypeOps.isLegalPrefix(prefix) then prefix else QualSkolemType(prefix)
398399
val space = readType()
399400
space.decl(name) match {
400-
case symd: SymDenotation if prefix.isArgPrefixOf(symd.symbol) => TypeRef(prefix, symd.symbol)
401+
case symd: SymDenotation if prefix.isArgPrefixOf(symd.symbol) => TypeRef(pre, symd.symbol)
401402
case _ => TypeRef(prefix, name, space.decl(name).asSeenFrom(prefix))
402403
}
403404
case REFINEDtype =>

compiler/src/dotty/tools/dotc/inlines/Inliner.scala

+38-15
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,39 @@ object Inliner:
131131
case _ => tree
132132
else super.transformInlined(tree)
133133
end InlinerMap
134+
135+
object OpaqueProxy:
136+
137+
def apply(ref: TermRef, cls: ClassSymbol, span: Span)(using Context): TermRef =
138+
def openOpaqueAliases(selfType: Type): List[(Name, Type)] = selfType match
139+
case RefinedType(parent, rname, TypeAlias(alias)) =>
140+
val opaq = cls.info.member(rname).symbol
141+
if opaq.isOpaqueAlias then
142+
(rname, alias.stripLazyRef.asSeenFrom(ref, cls))
143+
:: openOpaqueAliases(parent)
144+
else Nil
145+
case _ => Nil
146+
val refinements = openOpaqueAliases(cls.givenSelfType)
147+
val refinedType = refinements.foldLeft(ref: Type): (parent, refinement) =>
148+
RefinedType(parent, refinement._1, TypeAlias(refinement._2))
149+
val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, refinedType, span)
150+
refiningSym.termRef
151+
152+
def unapply(refiningRef: TermRef)(using Context): Option[TermRef] =
153+
val refiningSym = refiningRef.symbol
154+
if refiningSym.name.is(InlineBinderName) && refiningSym.is(Synthetic, butNot=InlineProxy) then
155+
refiningRef.info match
156+
case refinedType: RefinedType => refinedType.stripRefinement match
157+
case ref: TermRef => Some(ref)
158+
case _ => None
159+
case _ => None
160+
else
161+
None
162+
163+
end OpaqueProxy
164+
165+
private[inlines] def newSym(name: Name, flags: FlagSet, info: Type, span: Span)(using Context): Symbol =
166+
newSymbol(ctx.owner, name, flags, info, coord = span)
134167
end Inliner
135168

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

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

194227
/** A binding for the parameter of an inline method. This is a `val` def for
195228
* by-value parameters and a `def` def for by-name parameters. `val` defs inherit
@@ -351,20 +384,9 @@ class Inliner(val call: tpd.Tree)(using Context):
351384
&& (forThisProxy || inlinedMethod.isContainedIn(cls))
352385
&& mapRef(ref).isEmpty
353386
then
354-
def openOpaqueAliases(selfType: Type): List[(Name, Type)] = selfType match
355-
case RefinedType(parent, rname, TypeAlias(alias)) =>
356-
val opaq = cls.info.member(rname).symbol
357-
if opaq.isOpaqueAlias then
358-
(rname, alias.stripLazyRef.asSeenFrom(ref, cls))
359-
:: openOpaqueAliases(parent)
360-
else Nil
361-
case _ =>
362-
Nil
363-
val refinements = openOpaqueAliases(cls.givenSelfType)
364-
val refinedType = refinements.foldLeft(ref: Type) ((parent, refinement) =>
365-
RefinedType(parent, refinement._1, TypeAlias(refinement._2))
366-
)
367-
val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, refinedType).asTerm
387+
val refiningRef = OpaqueProxy(ref, cls, call.span)
388+
val refiningSym = refiningRef.symbol.asTerm
389+
val refinedType = refiningRef.info
368390
val refiningDef = ValDef(refiningSym, tpd.ref(ref).cast(refinedType), inferred = true).withSpan(span)
369391
inlining.println(i"add opaque alias proxy $refiningDef for $ref in $tp")
370392
bindingsBuf += refiningDef
@@ -768,6 +790,7 @@ class Inliner(val call: tpd.Tree)(using Context):
768790
override def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {
769791
val locked = ctx.typerState.ownedVars
770792
val qual1 = typed(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
793+
selectionType(tree, qual1) // side-effect
771794
val resNoReduce = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
772795
val reducedProjection = reducer.reduceProjection(resNoReduce)
773796
if reducedProjection.isType then

compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ object ResolveSuper {
109109
sym = other.symbol
110110
// Having a matching denotation is not enough: it should also be a subtype
111111
// of the superaccessor's type, see i5433.scala for an example where this matters
112-
val otherTp = other.asSeenFrom(base.typeRef).info
113-
val accTp = acc.asSeenFrom(base.typeRef).info
112+
val otherTp = other.asSeenFrom(base.thisType).info
113+
val accTp = acc.asSeenFrom(base.thisType).info
114114
// Since the super class can be Java defined,
115115
// we use relaxed overriding check for explicit nulls if one of the symbols is Java defined.
116116
// This forces `Null` to be a subtype of non-primitive value types during override checking.

tests/pos/i22062.scala

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class Label
2+
class Component
3+
4+
trait RenderableCellsCompanion:
5+
type Renderer[-A] <: CellRenderer[A]
6+
type DefaultRenderer[-A] <: Label & Renderer[A]
7+
8+
trait CellRendererCompanion:
9+
type CellInfo
10+
def labeled[A](): DefaultRenderer[A]
11+
protected trait LabelRenderer[-A] extends CellRenderer[A]:
12+
override abstract def componentFor(info: companion.CellInfo): Component = super.componentFor(info)
13+
14+
trait CellRenderer[-A]:
15+
val companion: CellRendererCompanion
16+
def componentFor(cellInfo: companion.CellInfo): Component
17+
18+
sealed trait TreeRenderers extends RenderableCellsCompanion:
19+
this: Tree.type =>
20+
21+
trait Renderer[-A] extends CellRenderer[A]:
22+
final override val companion = Renderer
23+
24+
object Renderer extends CellRendererCompanion:
25+
final override class CellInfo
26+
override def labeled[A]() = new DefaultRenderer[A] with LabelRenderer[A] {}
27+
28+
class DefaultRenderer[-A] extends Label with Renderer[A]:
29+
override def componentFor(info: Renderer.CellInfo): Component = ???
30+
31+
class Tree extends Component
32+
object Tree extends TreeRenderers

tests/pos/i22068.less-min.scala

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class A
2+
class B extends A
3+
class C extends A
4+
5+
object foos:
6+
opaque type Tag[A] = String
7+
object Tag:
8+
inline given mkTag[A]: Tag[A] = ???
9+
type Full[A] = Tag[A] | Set[A]
10+
sealed trait Set[A] extends Any
11+
case class Union[A](tags: Seq[Tag[Any]]) extends AnyVal with Set[A]:
12+
infix def and[B](t2: Full[B]): Unit = ???
13+
object Union:
14+
inline given mkUnion[A]: Union[A] = ???
15+
import foos.Tag.*
16+
17+
class Test:
18+
inline def m1[K1, K2](using b1: Union[K1], b2: Union[K2]): Unit =
19+
b1.and(b2)
20+
21+
def t1(): Unit = m1[B | C, A]

tests/pos/i22068.orig.scala

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
trait AnyFreeSpecLike:
2+
inline implicit def convertToFreeSpecStringWrapper(s: String): FreeSpecStringWrapper = ???
3+
protected final class FreeSpecStringWrapper(string: String):
4+
infix def in(testFun: => Any): Unit = ???
5+
6+
7+
import types.Tag.*
8+
class TagTest extends AnyFreeSpecLike{
9+
inline def test[T1, T2](using k1: Union[T1], k2: Union[T2]): Unit =
10+
"T1 <:< T2" in {
11+
val kresult = k1 <:< k2
12+
???
13+
}
14+
class A
15+
class B extends A
16+
class C extends A
17+
test[B | C, A]
18+
}
19+
20+
object types:
21+
opaque type Tag[A] = String
22+
object Tag:
23+
inline given apply[A]: Tag[A] = ???
24+
type Full[A] = Tag[A] | Set[A]
25+
sealed trait Set[A] extends Any
26+
case class Union[A](tags: Seq[Tag[Any]]) extends AnyVal with Set[A]:
27+
infix def <:<[B](t2: Full[B]): Boolean = ???
28+
object Union:
29+
inline given apply[A]: Union[A] = ???

tests/pos/i22068.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object foos:
2+
opaque type Foo[T] = String
3+
object bars:
4+
class Bar1[A] { def and(b: Bar2): Unit = () }
5+
class Bar2
6+
inline def mkBar1[A]: Bar1[A] = new Bar1[A]
7+
def mkBar2 : Bar2 = new Bar2
8+
import foos.*, bars.*
9+
10+
class Test:
11+
inline def m1[X](b1: Bar1[X], b2: Bar2): Unit =
12+
b1.and(b2)
13+
14+
def t1(): Unit = m1(mkBar1[Int], mkBar2)

tests/pos/i22070/macro.scala

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait Featureful[T]:
2+
def toFeatures(value: T): IArray[Float]
3+
4+
object Featureful:
5+
inline def derived[T](using scala.deriving.Mirror.Of[T]) = ${ derivedImpl[T] }
6+
7+
import scala.quoted.*
8+
private def derivedImpl[T: Type](using Quotes): Expr[Featureful[T]] =
9+
import quotes.reflect.*
10+
'{
11+
new Featureful[T]:
12+
def toFeatures(value: T) =
13+
val feats = IArray.empty[Featureful[?]]
14+
val product = value.asInstanceOf[Product]
15+
product.productIterator.zipWithIndex.foreach: (any, idx) =>
16+
feats(idx).toFeatures(any.asInstanceOf)
17+
IArray.empty
18+
}

tests/pos/i22070/usage.scala

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
case class Breaks(x: Boolean, y: Boolean) derives Featureful

0 commit comments

Comments
 (0)