From e116c82a369b7588d222c2d92b6945467fcec2fd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 6 Nov 2024 15:32:10 +0000 Subject: [PATCH 1/3] Move makePackageObjPrefixExplicit to TypeUtils --- .../dotty/tools/dotc/core/TypeErasure.scala | 1 - .../src/dotty/tools/dotc/core/TypeOps.scala | 30 ---------------- .../src/dotty/tools/dotc/core/TypeUtils.scala | 36 ++++++++++++++++++- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- 5 files changed, 37 insertions(+), 34 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 17ddc93a10f2..6f89f3c84911 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -5,7 +5,6 @@ package core import Symbols.*, Types.*, Contexts.*, Flags.*, Names.*, StdNames.*, Phases.* import Flags.JavaDefined import Uniques.unique -import TypeOps.makePackageObjPrefixExplicit import backend.sjs.JSDefinitions import transform.ExplicitOuter.* import transform.ValueClasses.* diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 9c9bf332d1f4..d801362fd475 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -573,36 +573,6 @@ object TypeOps: widenMap(tp) } - /** If `tpe` is of the form `p.x` where `p` refers to a package - * but `x` is not owned by a package, expand it to - * - * p.package.x - */ - def makePackageObjPrefixExplicit(tpe: NamedType)(using Context): Type = { - def tryInsert(pkgClass: SymDenotation): Type = pkgClass match { - case pkg: PackageClassDenotation => - var sym = tpe.symbol - if !sym.exists && tpe.denot.isOverloaded then - // we know that all alternatives must come from the same package object, since - // otherwise we would get "is already defined" errors. So we can take the first - // symbol we see. - sym = tpe.denot.alternatives.head.symbol - val pobj = pkg.packageObjFor(sym) - if (pobj.exists) tpe.derivedSelect(pobj.termRef) - else tpe - case _ => - tpe - } - if (tpe.symbol.isRoot) - tpe - else - tpe.prefix match { - case pre: ThisType if pre.cls.is(Package) => tryInsert(pre.cls) - case pre: TermRef if pre.symbol.is(Package) => tryInsert(pre.symbol.moduleClass) - case _ => tpe - } - } - /** An argument bounds violation is a triple consisting of * - the argument tree * - a string "upper" or "lower" indicating which bound is violated diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index 8167bf532e1a..fa4e3e31ec32 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -3,7 +3,10 @@ package dotc package core import TypeErasure.ErasedValueType -import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.* +import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*, SymDenotations.* +import Names.{Name, TermName} +import Constants.Constant + import Names.Name import StdNames.nme @@ -127,6 +130,37 @@ class TypeUtils: case mt: MethodType => mt.isImplicitMethod || mt.resType.takesImplicitParams case _ => false + + /** If `self` is of the form `p.x` where `p` refers to a package + * but `x` is not owned by a package, expand it to + * + * p.package.x + */ + def makePackageObjPrefixExplicit(using Context): Type = + def tryInsert(tpe: NamedType, pkgClass: SymDenotation): Type = pkgClass match + case pkg: PackageClassDenotation => + var sym = tpe.symbol + if !sym.exists && tpe.denot.isOverloaded then + // we know that all alternatives must come from the same package object, since + // otherwise we would get "is already defined" errors. So we can take the first + // symbol we see. + sym = tpe.denot.alternatives.head.symbol + val pobj = pkg.packageObjFor(sym) + if pobj.exists then tpe.derivedSelect(pobj.termRef) + else tpe + case _ => + tpe + self match + case tpe: NamedType => + if tpe.symbol.isRoot then + tpe + else + tpe.prefix match + case pre: ThisType if pre.cls.is(Package) => tryInsert(tpe, pre.cls) + case pre: TermRef if pre.symbol.is(Package) => tryInsert(tpe, pre.symbol.moduleClass) + case _ => tpe + case tpe => tpe + /** The constructors of this type that are applicable to `argTypes`, without needing * an implicit conversion. Curried constructors are always excluded. * @param adaptVarargs if true, allow a constructor with just a varargs argument to diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e76b333428f5..23fcc4da861b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1189,7 +1189,7 @@ class TreeUnpickler(reader: TastyReader, val tpe0 = name match case name: TypeName => TypeRef(qualType, name, denot) case name: TermName => TermRef(qualType, name, denot) - val tpe = TypeOps.makePackageObjPrefixExplicit(tpe0) + val tpe = tpe0.makePackageObjPrefixExplicit ConstFold.Select(untpd.Select(qual, name).withType(tpe)) def completeSelect(name: Name, sig: Signature, target: Name): Select = diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index c764b035cba1..5b5a59335184 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -81,7 +81,7 @@ trait TypeAssigner { defn.FromJavaObjectType else tpe match case tpe: NamedType => - val tpe1 = TypeOps.makePackageObjPrefixExplicit(tpe) + val tpe1 = tpe.makePackageObjPrefixExplicit if tpe1 ne tpe then accessibleType(tpe1, superAccess) else From e3c6063199c9382d3bd93d3a9a7381ef880dc598 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Wed, 19 Feb 2025 18:42:53 +0100 Subject: [PATCH 2/3] Move makePackageObjPrefixExplicit to TypeUtils [Cherry-picked f9145d73a000cc8391cf2fe3e2b9fe1c0b4683a7][modified] From 3ea3a21bd0a15ce441dc7f04039012190e75243d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 1 Sep 2024 23:57:29 +0100 Subject: [PATCH 3/3] Fix pkg obj prefix of opaque tp ext meth There is use of `makePackageObjPrefixExplicit` within `accessibleType`, which is called on the result of findRef in typedIdent. But in `tryExtension` it's no such use. We could fix it in the usage of the results in `tryExtension`, but I thought perhaps we could fix it for all call sites, by handling it within findRef. [Cherry-picked 7db83c547b6e950fe2ad28fdd3007f8f8cb0bd35] --- .../src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- tests/pos/i18097.1.scala | 22 +++++++++++++++++++ tests/pos/i18097.2.scala | 13 +++++++++++ tests/pos/i18097.2.works.scala | 13 +++++++++++ tests/pos/i18097.3/Opaque.scala | 9 ++++++++ tests/pos/i18097.3/Test.scala | 13 +++++++++++ tests/pos/i18097.orig.scala | 20 +++++++++++++++++ 7 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18097.1.scala create mode 100644 tests/pos/i18097.2.scala create mode 100644 tests/pos/i18097.2.works.scala create mode 100644 tests/pos/i18097.3/Opaque.scala create mode 100644 tests/pos/i18097.3/Test.scala create mode 100644 tests/pos/i18097.orig.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 159e93dfc9fb..5cf447eb2a48 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -306,7 +306,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // so we ignore that import. if reallyExists(denot) && !isScalaJsPseudoUnion then if unimported.isEmpty || !unimported.contains(pre.termSymbol) then - return pre.select(name, denot) + return pre.select(name, denot).makePackageObjPrefixExplicit case _ => if imp.importSym.isCompleting then report.warning(i"cyclic ${imp.importSym}, ignored", pos) @@ -466,7 +466,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer defDenot.symbol.owner else curOwner - effectiveOwner.thisType.select(name, defDenot) + effectiveOwner.thisType.select(name, defDenot).makePackageObjPrefixExplicit } if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry diff --git a/tests/pos/i18097.1.scala b/tests/pos/i18097.1.scala new file mode 100644 index 000000000000..b7b57467e3b0 --- /dev/null +++ b/tests/pos/i18097.1.scala @@ -0,0 +1,22 @@ +opaque type Pos = Double + +object Pos: + extension (x: Pos) + def mult1(y: Pos): Pos = x * y + +extension (x: Pos) + def mult2(y: Pos): Pos = x * y + +class Test: + def test(key: String, a: Pos, b: Pos): Unit = + val tup1 = new Tuple1(Pos.mult1(a)(b)) + val res1: Pos = tup1._1 + + val tup2 = new Tuple1(a.mult1(b)) + val res2: Pos = tup2._1 + + val tup3 = new Tuple1(mult2(a)(b)) + val res3: Pos = tup3._1 + + val tup4 = new Tuple1(a.mult2(b)) + val res4: Pos = tup4._1 // was error: Found: (tup4._4 : Double) Required: Pos diff --git a/tests/pos/i18097.2.scala b/tests/pos/i18097.2.scala new file mode 100644 index 000000000000..c676479aab42 --- /dev/null +++ b/tests/pos/i18097.2.scala @@ -0,0 +1,13 @@ +opaque type Namespace = List[String] + +object Namespace: + def apply(head: String): Namespace = List(head) + +extension (ns: Namespace) + def appended(segment: String): Namespace = ns.appended(segment) + +object Main: + def main(args: Array[String]): Unit = + val a: Namespace = Namespace("a") + .appended("B") + .appended("c") // was error: Found: List[String] Required: Namespace diff --git a/tests/pos/i18097.2.works.scala b/tests/pos/i18097.2.works.scala new file mode 100644 index 000000000000..3ba8e056a4a5 --- /dev/null +++ b/tests/pos/i18097.2.works.scala @@ -0,0 +1,13 @@ +object Main: + opaque type Namespace = List[String] + + object Namespace: + def apply(head: String): Namespace = List(head) + + extension (ns: Namespace) + def appended(segment: String): Namespace = ns.appended(segment) + + def main(args: Array[String]): Unit = + val a: Namespace = Namespace("a") + .appended("B") + .appended("c") diff --git a/tests/pos/i18097.3/Opaque.scala b/tests/pos/i18097.3/Opaque.scala new file mode 100644 index 000000000000..cb9c9eaedfb3 --- /dev/null +++ b/tests/pos/i18097.3/Opaque.scala @@ -0,0 +1,9 @@ +package test + +type Foo = Unit +val bar: Foo = () + +opaque type Opaque = Unit + +extension (foo: Foo) + def go: Option[Opaque] = ??? diff --git a/tests/pos/i18097.3/Test.scala b/tests/pos/i18097.3/Test.scala new file mode 100644 index 000000000000..38f2199944c2 --- /dev/null +++ b/tests/pos/i18097.3/Test.scala @@ -0,0 +1,13 @@ +package test + +final case class Test(value: Opaque) + +def test: Test = + bar.go match + case Some(value) => Test(value) // was error: Found: (value : Unit) Required: test.Opaque + case _ => ??? + +def test2: Test = + go(bar) match + case Some(value) => Test(value) + case _ => ??? diff --git a/tests/pos/i18097.orig.scala b/tests/pos/i18097.orig.scala new file mode 100644 index 000000000000..092a904f6de4 --- /dev/null +++ b/tests/pos/i18097.orig.scala @@ -0,0 +1,20 @@ +opaque type PositiveNumber = Double + +object PositiveNumber: + extension (x: PositiveNumber) + def mult1(other: PositiveNumber): PositiveNumber = + x * other + +extension (x: PositiveNumber) + def mult2(other: PositiveNumber): PositiveNumber = + x * other + +object Test: + def multMap1[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult1(num)).toMap + + def multMap2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult2(num)).toMap // was error +// ^ +// Cannot prove that (A, Double) <:< (A, V2). +// +// where: V2 is a type variable with constraint <: PositiveNumber + def multMap2_2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> mult2(value)(num)).toMap