Skip to content

Commit 896965c

Browse files
authored
Fix pkg obj prefix of opaque tp ext meth (#21527)
2 parents 33b3d60 + 7db83c5 commit 896965c

12 files changed

+125
-36
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package core
55
import Symbols.*, Types.*, Contexts.*, Flags.*, Names.*, StdNames.*, Phases.*
66
import Flags.JavaDefined
77
import Uniques.unique
8-
import TypeOps.makePackageObjPrefixExplicit
98
import backend.sjs.JSDefinitions
109
import transform.ExplicitOuter.*
1110
import transform.ValueClasses.*

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

-30
Original file line numberDiff line numberDiff line change
@@ -560,36 +560,6 @@ object TypeOps:
560560
widenMap(tp)
561561
}
562562

563-
/** If `tpe` is of the form `p.x` where `p` refers to a package
564-
* but `x` is not owned by a package, expand it to
565-
*
566-
* p.package.x
567-
*/
568-
def makePackageObjPrefixExplicit(tpe: NamedType)(using Context): Type = {
569-
def tryInsert(pkgClass: SymDenotation): Type = pkgClass match {
570-
case pkg: PackageClassDenotation =>
571-
var sym = tpe.symbol
572-
if !sym.exists && tpe.denot.isOverloaded then
573-
// we know that all alternatives must come from the same package object, since
574-
// otherwise we would get "is already defined" errors. So we can take the first
575-
// symbol we see.
576-
sym = tpe.denot.alternatives.head.symbol
577-
val pobj = pkg.packageObjFor(sym)
578-
if (pobj.exists) tpe.derivedSelect(pobj.termRef)
579-
else tpe
580-
case _ =>
581-
tpe
582-
}
583-
if (tpe.symbol.isRoot)
584-
tpe
585-
else
586-
tpe.prefix match {
587-
case pre: ThisType if pre.cls.is(Package) => tryInsert(pre.cls)
588-
case pre: TermRef if pre.symbol.is(Package) => tryInsert(pre.symbol.moduleClass)
589-
case _ => tpe
590-
}
591-
}
592-
593563
/** An argument bounds violation is a triple consisting of
594564
* - the argument tree
595565
* - a string "upper" or "lower" indicating which bound is violated

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

+31-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotc
33
package core
44

55
import TypeErasure.ErasedValueType
6-
import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*
6+
import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*, SymDenotations.*
77
import Names.{Name, TermName}
88
import Constants.Constant
99

@@ -186,6 +186,36 @@ class TypeUtils:
186186
case self: Types.ThisType => self.cls == cls
187187
case _ => false
188188

189+
/** If `self` is of the form `p.x` where `p` refers to a package
190+
* but `x` is not owned by a package, expand it to
191+
*
192+
* p.package.x
193+
*/
194+
def makePackageObjPrefixExplicit(using Context): Type =
195+
def tryInsert(tpe: NamedType, pkgClass: SymDenotation): Type = pkgClass match
196+
case pkg: PackageClassDenotation =>
197+
var sym = tpe.symbol
198+
if !sym.exists && tpe.denot.isOverloaded then
199+
// we know that all alternatives must come from the same package object, since
200+
// otherwise we would get "is already defined" errors. So we can take the first
201+
// symbol we see.
202+
sym = tpe.denot.alternatives.head.symbol
203+
val pobj = pkg.packageObjFor(sym)
204+
if pobj.exists then tpe.derivedSelect(pobj.termRef)
205+
else tpe
206+
case _ =>
207+
tpe
208+
self match
209+
case tpe: NamedType =>
210+
if tpe.symbol.isRoot then
211+
tpe
212+
else
213+
tpe.prefix match
214+
case pre: ThisType if pre.cls.is(Package) => tryInsert(tpe, pre.cls)
215+
case pre: TermRef if pre.symbol.is(Package) => tryInsert(tpe, pre.symbol.moduleClass)
216+
case _ => tpe
217+
case tpe => tpe
218+
189219
/** Strip all outer refinements off this type */
190220
def stripRefinement: Type = self match
191221
case self: RefinedOrRecType => self.parent.stripRefinement

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ class TreeUnpickler(reader: TastyReader,
12731273
val tpe0 = name match
12741274
case name: TypeName => TypeRef(qualType, name, denot)
12751275
case name: TermName => TermRef(qualType, name, denot)
1276-
val tpe = TypeOps.makePackageObjPrefixExplicit(tpe0)
1276+
val tpe = tpe0.makePackageObjPrefixExplicit
12771277
ConstFold.Select(untpd.Select(qual, name).withType(tpe))
12781278

12791279
def completeSelect(name: Name, sig: Signature, target: Name): Select =

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ trait TypeAssigner {
8585
defn.FromJavaObjectType
8686
else tpe match
8787
case tpe: NamedType =>
88-
val tpe1 = TypeOps.makePackageObjPrefixExplicit(tpe)
88+
val tpe1 = tpe.makePackageObjPrefixExplicit
8989
if tpe1 ne tpe then
9090
accessibleType(tpe1, superAccess)
9191
else

compiler/src/dotty/tools/dotc/typer/Typer.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
344344
// so we ignore that import.
345345
if reallyExists(denot) && !isScalaJsPseudoUnion then
346346
if unimported.isEmpty || !unimported.contains(pre.termSymbol) then
347-
return pre.select(name, denot)
347+
return pre.select(name, denot).makePackageObjPrefixExplicit
348348
case _ =>
349349
if imp.importSym.isCompleting then
350350
report.warning(i"cyclic ${imp.importSym}, ignored", pos)
@@ -504,7 +504,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
504504
defDenot.symbol.owner
505505
else
506506
curOwner
507-
effectiveOwner.thisType.select(name, defDenot)
507+
effectiveOwner.thisType.select(name, defDenot).makePackageObjPrefixExplicit
508508
}
509509
if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then
510510
result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry

tests/pos/i18097.1.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
opaque type Pos = Double
2+
3+
object Pos:
4+
extension (x: Pos)
5+
def mult1(y: Pos): Pos = x * y
6+
7+
extension (x: Pos)
8+
def mult2(y: Pos): Pos = x * y
9+
10+
class Test:
11+
def test(key: String, a: Pos, b: Pos): Unit =
12+
val tup1 = new Tuple1(Pos.mult1(a)(b))
13+
val res1: Pos = tup1._1
14+
15+
val tup2 = new Tuple1(a.mult1(b))
16+
val res2: Pos = tup2._1
17+
18+
val tup3 = new Tuple1(mult2(a)(b))
19+
val res3: Pos = tup3._1
20+
21+
val tup4 = new Tuple1(a.mult2(b))
22+
val res4: Pos = tup4._1 // was error: Found: (tup4._4 : Double) Required: Pos

tests/pos/i18097.2.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
opaque type Namespace = List[String]
2+
3+
object Namespace:
4+
def apply(head: String): Namespace = List(head)
5+
6+
extension (ns: Namespace)
7+
def appended(segment: String): Namespace = ns.appended(segment)
8+
9+
object Main:
10+
def main(args: Array[String]): Unit =
11+
val a: Namespace = Namespace("a")
12+
.appended("B")
13+
.appended("c") // was error: Found: List[String] Required: Namespace

tests/pos/i18097.2.works.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Main:
2+
opaque type Namespace = List[String]
3+
4+
object Namespace:
5+
def apply(head: String): Namespace = List(head)
6+
7+
extension (ns: Namespace)
8+
def appended(segment: String): Namespace = ns.appended(segment)
9+
10+
def main(args: Array[String]): Unit =
11+
val a: Namespace = Namespace("a")
12+
.appended("B")
13+
.appended("c")

tests/pos/i18097.3/Opaque.scala

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test
2+
3+
type Foo = Unit
4+
val bar: Foo = ()
5+
6+
opaque type Opaque = Unit
7+
8+
extension (foo: Foo)
9+
def go: Option[Opaque] = ???

tests/pos/i18097.3/Test.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test
2+
3+
final case class Test(value: Opaque)
4+
5+
def test: Test =
6+
bar.go match
7+
case Some(value) => Test(value) // was error: Found: (value : Unit) Required: test.Opaque
8+
case _ => ???
9+
10+
def test2: Test =
11+
go(bar) match
12+
case Some(value) => Test(value)
13+
case _ => ???

tests/pos/i18097.orig.scala

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
opaque type PositiveNumber = Double
2+
3+
object PositiveNumber:
4+
extension (x: PositiveNumber)
5+
def mult1(other: PositiveNumber): PositiveNumber =
6+
x * other
7+
8+
extension (x: PositiveNumber)
9+
def mult2(other: PositiveNumber): PositiveNumber =
10+
x * other
11+
12+
object Test:
13+
def multMap1[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult1(num)).toMap
14+
15+
def multMap2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult2(num)).toMap // was error
16+
// ^
17+
// Cannot prove that (A, Double) <:< (A, V2).
18+
//
19+
// where: V2 is a type variable with constraint <: PositiveNumber
20+
def multMap2_2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> mult2(value)(num)).toMap

0 commit comments

Comments
 (0)