Skip to content

Commit 881ae82

Browse files
authored
Merge branch 'main' into string-singleton-comparisons
2 parents 07bb86c + f67a1a0 commit 881ae82

File tree

20 files changed

+303
-38
lines changed

20 files changed

+303
-38
lines changed

.github/workflows/lts-backport.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
with:
1717
fetch-depth: 0
1818
- uses: coursier/cache-action@v7
19-
- uses: VirtusLab/scala-cli-setup@v1.10.1
19+
- uses: VirtusLab/scala-cli-setup@v1.11.0
2020
- run: scala-cli ./project/scripts/addToBackportingProject.scala -- ${{ github.sha }}
2121
env:
2222
GRAPHQL_API_TOKEN: ${{ secrets.GRAPHQL_API_TOKEN }}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,9 @@ object NamerOps:
223223
companion
224224

225225
def typeConstructorCompanion(tsym: Symbol, prefix: Type, proxy: Symbol)(using Context): TermSymbol =
226-
newSymbol(tsym.owner, tsym.name.toTermName,
227-
ConstructorCompanionFlags | StableRealizable | Method, ExprType(prefix.select(proxy)), coord = tsym.coord)
226+
inline def core = ConstructorCompanionFlags | StableRealizable | Method
227+
inline def flags = if tsym.is(Exported) then core | Exported else core
228+
newSymbol(tsym.owner, tsym.name.toTermName, flags, ExprType(prefix.select(proxy)), coord = tsym.coord)
228229

229230
/** Add all necessary constructor proxy symbols for members of class `cls`. This means:
230231
*

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,29 @@ trait Applications extends Compatibility {
883883
case SAMType(samMeth, samParent) => argtpe <:< samMeth.toFunctionType(isJava = samParent.classSymbol.is(JavaDefined))
884884
case _ => false
885885

886-
isCompatible(argtpe, formal)
886+
// For overload resolution, allow wildcard upper bounds to match their bound type.
887+
// For example, given:
888+
// def blub[T](a: Class[? <: T]): String = "a"
889+
// def blub[T](a: Class[T], ints: Int*): String = "b"
890+
// blub(classOf[Object])
891+
//
892+
// The non-varargs overload should be preferred. While Class[? <: T] is not a
893+
// subtype of Class[T] (Class is invariant), for overload resolution we consider
894+
// Class[? <: T] "applicable" where Class[T] is expected by checking if the
895+
// wildcard's upper bound is a subtype of the formal type parameter.
896+
def wildcardArgOK =
897+
argtpe match
898+
case at @ AppliedType(tycon1, args1) if at.hasWildcardArg =>
899+
formal match
900+
case AppliedType(tycon2, args2)
901+
if tycon1 =:= tycon2 && args1.length == args2.length =>
902+
args1.lazyZip(args2).forall {
903+
case (TypeBounds(_, hi), formal) => hi relaxed_<:< formal
904+
}
905+
case _ => false
906+
case _ => false
907+
908+
isCompatible(argtpe, formal) || wildcardArgOK
887909
// Only allow SAM-conversion to PartialFunction if implicit conversions
888910
// are enabled. This is necessary to avoid ambiguity between an overload
889911
// taking a PartialFunction and one taking a Function1 because

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,8 +1262,8 @@ class Namer { typer: Typer =>
12621262
n += 1
12631263

12641264
/** Add a forwarder with name `alias` or its type name equivalent to `mbr`,
1265-
* provided `mbr` is accessible and of the right implicit/non-implicit kind.
1266-
*/
1265+
* provided `mbr` is accessible and of the right implicit/non-implicit kind.
1266+
*/
12671267
def addForwarder(alias: TermName, mbr: SingleDenotation, span: Span): Unit =
12681268

12691269
def adaptForwarderParams(acc: List[List[tpd.Tree]], tp: Type, prefss: List[List[tpd.Tree]])

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

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,10 +1258,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
12581258
pt)
12591259
case _ =>
12601260
var tpt1 = typedType(tree.tpt)
1261-
val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol
12621261
if ctx.mode.isQuotedPattern && tpt1.tpe.typeSymbol.isAllOf(Synthetic | Case) then
12631262
val errorTp = errorType(CannotInstantiateQuotedTypeVar(tpt1.tpe.typeSymbol), tpt1.srcPos)
12641263
return cpy.New(tree)(tpt1).withType(errorTp)
1264+
val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol
12651265
if tsym.is(Package) then
12661266
report.error(em"$tsym cannot be instantiated", tpt1.srcPos)
12671267
tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.srcPos))
@@ -1271,7 +1271,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
12711271
report.error(WildcardOnTypeArgumentNotAllowedOnNew(), targ.srcPos)
12721272
case _ =>
12731273
}
1274-
12751274
assignType(cpy.New(tree)(tpt1), tpt1)
12761275
}
12771276

@@ -1876,6 +1875,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
18761875
*/
18771876
var paramIndex = Map[Name, Int]()
18781877

1878+
def containsParamRef(tree: untpd.Tree, params: List[untpd.ValDef]): Boolean =
1879+
import untpd.*
1880+
val acc = new UntypedTreeAccumulator[Boolean]:
1881+
def apply(x: Boolean, t: Tree)(using Context) =
1882+
if x then true
1883+
else t match
1884+
case _: untpd.TypedSplice => false
1885+
case Ident(name) => params.exists(_.name == name)
1886+
case _ => foldOver(x, t)
1887+
acc(false, tree)
1888+
18791889
/** Infer parameter type from the body of the function
18801890
*
18811891
* 1. If function is of the form
@@ -1910,42 +1920,44 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
19101920
for (param <- params; idx <- paramIndices(param, args))
19111921
yield param.name -> idx
19121922
}.toMap
1913-
if (paramIndex.size == params.length)
1923+
if (paramIndex.size == params.length) then
19141924
expr match
19151925
case untpd.TypedSplice(expr1) =>
19161926
expr1.tpe
1917-
case _ =>
1927+
case _ if !containsParamRef(expr, params) =>
19181928
val outerCtx = ctx
19191929
val nestedCtx = outerCtx.fresh.setNewTyperState()
1920-
inContext(nestedCtx) {
1921-
val protoArgs = args.map(_.withType(WildcardType))
1930+
inContext(nestedCtx):
1931+
// try to type expr with fresh unknown arguments.
1932+
val protoArgs = args.map(arg => untpd.Ident(UniqueName.fresh()).withSpan(arg.span))
19221933
val callProto = FunProto(protoArgs, WildcardType)(this, app.applyKind)
19231934
val expr1 = typedExpr(expr, callProto)
19241935
if nestedCtx.reporter.hasErrors then NoType
1925-
else inContext(outerCtx) {
1936+
else inContext(outerCtx):
19261937
nestedCtx.typerState.commit()
19271938
fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
19281939
expr1.tpe
1929-
}
1930-
}
1940+
case _ =>
1941+
NoType
19311942
else NoType
19321943
case _ =>
19331944
NoType
19341945
}
19351946

1936-
/** Try to instantiate one type variable bounded by function types that appear
1947+
/** Find one instantiatable type variable bounded by function types that appear
19371948
* deeply inside `tp`, including union or intersection types.
19381949
*/
1939-
def tryToInstantiateDeeply(tp: Type): Boolean = tp.dealias match
1950+
def instantiatableTypeVar(tp: Type): Type = tp.dealias match
19401951
case tp: AndOrType =>
1941-
tryToInstantiateDeeply(tp.tp1)
1942-
|| tryToInstantiateDeeply(tp.tp2)
1952+
val t1 = instantiatableTypeVar(tp.tp1)
1953+
if t1.exists then t1
1954+
else instantiatableTypeVar(tp.tp2)
19431955
case tp: FlexibleType =>
1944-
tryToInstantiateDeeply(tp.hi)
1956+
instantiatableTypeVar(tp.hi)
19451957
case tp: TypeVar if isConstrainedByFunctionType(tp) =>
19461958
// Only instantiate if the type variable is constrained by function types
1947-
isFullyDefined(tp, ForceDegree.flipBottom)
1948-
case _ => false
1959+
tp
1960+
case _ => NoType
19491961

19501962
def isConstrainedByFunctionType(tvar: TypeVar): Boolean =
19511963
val origin = tvar.origin
@@ -1961,16 +1973,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
19611973
case _ => false
19621974
containsFunctionType(bounds.lo) || containsFunctionType(bounds.hi)
19631975

1964-
if untpd.isFunctionWithUnknownParamType(tree) && !calleeType.exists then
1976+
if untpd.isFunctionWithUnknownParamType(tree) then
19651977
// Try to instantiate `pt` when possible.
19661978
// * If `pt` is a type variable, we try to instantiate it directly.
19671979
// * If `pt` is a more complex type, we try to instantiate it deeply by searching
19681980
// a nested type variable bounded by a function type to help infer parameter types.
19691981
// If it does not work the error will be reported later in `inferredParam`,
19701982
// when we try to infer the parameter type.
1971-
pt match
1972-
case pt: TypeVar => isFullyDefined(pt, ForceDegree.flipBottom)
1973-
case _ => tryToInstantiateDeeply(pt)
1983+
// Note: we only check the `calleeType` if there is a TypeVar to instantiate to
1984+
// prioritize inferring from the callee.
1985+
val tp = if pt.isInstanceOf[TypeVar] then pt else instantiatableTypeVar(pt)
1986+
if tp.exists && !calleeType.exists then
1987+
isFullyDefined(tp, ForceDegree.flipBottom)
19741988

19751989
val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos)
19761990

@@ -5010,7 +5024,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
50105024
case _ =>
50115025
}
50125026

5013-
/** If `tree` is a constructor proxy reference, convert it to a `new` expression,
5027+
/** If `tree` is a constructor proxy reference, convert it to a `new` expression;
5028+
* check if it is a reference to an exported type/companion pair;
50145029
* otherwise return EmptyTree.
50155030
*/
50165031
def newExpr(tree: Tree): Tree =
@@ -5026,13 +5041,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
50265041
cpy.Ident(qual)(qual.symbol.name.sourceModuleName.toTypeName)
50275042
case _ =>
50285043
errorTree(tree, em"cannot convert from $tree to an instance creation expression")
5029-
val tycon = ctorResultType.underlyingClassRef(refinementOK = Feature.enabled(modularity))
5044+
val tycon =
5045+
val exported =
5046+
if qual.symbol.isAllOf(SyntheticMethod | Exported) then
5047+
qual.symbol.owner.info.memberBasedOnFlags(qual.symbol.name.toTypeName, required = Exported)
5048+
else NoDenotation
5049+
if exported.exists then exported.symbol.typeRef
5050+
else ctorResultType.underlyingClassRef(refinementOK = Feature.enabled(modularity))
50305051
typed(
50315052
untpd.Select(
50325053
untpd.New(untpd.TypedSplice(tpt.withType(tycon))),
50335054
nme.CONSTRUCTOR),
5034-
pt)
5035-
.showing(i"convert creator $tree -> $result", typr)
5055+
pt
5056+
).showing(i"convert creator $tree -> $result", typr)
50365057

50375058
/** If `tree` is a constructor proxy reference, return the type it constructs,
50385059
* otherwise return NoType.
@@ -5051,7 +5072,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
50515072
// begin adapt1
50525073
tree match {
50535074
case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[?] | _: Closure => tree
5054-
case _ => tree.tpe.widen match {
5075+
case _ =>
5076+
tree.tpe.widen match
50555077
case tp: FlexType =>
50565078
ensureReported(tp)
50575079
tree
@@ -5113,7 +5135,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
51135135
if (ctx.mode is Mode.Type) adaptType(tree.tpe)
51145136
else adaptNoArgs(wtp)
51155137
}
5116-
}
51175138
}
51185139
}
51195140

library/src/scala/IArray.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ object IArray:
306306
def stepper[S <: Stepper[?]](using StepperShape[T, S]): S = genericArrayOps(arr).stepper[S]
307307
def tails: Iterator[IArray[T]] = genericArrayOps(arr).tails
308308
def tapEach[U](f: (T) => U): IArray[T] =
309-
arr.toSeq.foreach(f)
309+
IArray.genericWrapArray(arr).foreach(f) // just to be sure it doesnt clone
310310
arr
311311
def transpose[U](implicit asArray: T => IArray[U]): IArray[IArray[U]] =
312312
genericArrayOps(arr).transpose(using asArray.asInstanceOf[T => Array[U]])

presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,15 +529,18 @@ object XRayModeHint:
529529
case apply @ Apply(inner, _)
530530
if !apply.span.isZeroExtent && inner.sourcePos.exists && !isParentOnSameLine &&
531531
!isParentApply && endsInSimpleSelect(apply) && isEndOfLine(tree.sourcePos) =>
532-
Some((apply.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
532+
Some((apply.tpe.widen.finalResultType.deepDealiasAndSimplify, tree.sourcePos))
533533
/*
534534
innerTree
535535
.select
536536
*/
537537
case select @ Select(innerTree, _)
538538
if !select.span.isZeroExtent && innerTree.sourcePos.exists &&
539539
!isParentOnSameLine && !isParentApply && isEndOfLine(tree.sourcePos) =>
540-
Some((select.tpe.widen.deepDealiasAndSimplify, tree.sourcePos))
540+
val tpe = parent match
541+
case Some(ta: TypeApply) => ta.tpe
542+
case _ => select.tpe
543+
Some((tpe.widen.finalResultType.deepDealiasAndSimplify, tree.sourcePos))
541544
case _ => None
542545
else None
543546

presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,4 +1687,40 @@ class InlayHintsSuite extends BaseInlayHintsSuite {
16871687
|}
16881688
|""".stripMargin
16891689
)
1690+
1691+
@Test def `xray-metals-i8021` =
1692+
check(
1693+
"""|object Main:
1694+
|
1695+
| case class Order(id: String, amount: BigDecimal)
1696+
| case class User(name: String, orders: List[Order])
1697+
|
1698+
| val users = List(
1699+
| User("Alice", List(Order("A1", 100), Order("A2", 50))),
1700+
| User("Bob", List(Order("B1", 200)))
1701+
| )
1702+
|
1703+
| val total = users
1704+
| .filter(_.name.startsWith("A"))
1705+
| .flatMap(_.orders)
1706+
| .map(_.amount)
1707+
| .sum
1708+
|""".stripMargin,
1709+
"""|object Main:
1710+
|
1711+
| case class Order(id: String, amount: BigDecimal)
1712+
| case class User(name: String, orders: List[Order])
1713+
|
1714+
| val users/*: List<<scala/collection/immutable/List#>>[User<<(4:13)>>]*/ = List/*[User<<(4:13)>>]*/(
1715+
| /*elems = */User(/*name = */"Alice", /*orders = */List/*[Order<<(3:13)>>]*/(/*elems = */Order(/*id = */"A1", /*int2bigDecimal<<scala/math/BigDecimal.int2bigDecimal().>>(*//*amount = */100/*)*/), Order(/*id = */"A2", /*int2bigDecimal<<scala/math/BigDecimal.int2bigDecimal().>>(*//*amount = */50/*)*/))),
1716+
| User(/*name = */"Bob", /*orders = */List/*[Order<<(3:13)>>]*/(/*elems = */Order(/*id = */"B1", /*int2bigDecimal<<scala/math/BigDecimal.int2bigDecimal().>>(*//*amount = */200/*)*/)))
1717+
| )
1718+
|
1719+
| val total/*: BigDecimal<<scala/math/BigDecimal#>>*/ = users
1720+
| .filter(/*p = */_.name.startsWith("A"))/*: List<<scala/collection/immutable/List#>>[User<<(4:13)>>]*/
1721+
| .flatMap/*[Order<<(3:13)>>]*/(/*f = */_.orders)/* : List<<scala/collection/immutable/List#>>[Order<<(3:13)>>]*/
1722+
| .map/*[BigDecimal<<scala/math/BigDecimal#>>]*/(/*f = */_.amount)/* : List<<scala/collection/immutable/List#>>[BigDecimal<<scala/math/BigDecimal#>>]*/
1723+
| .sum/*[BigDecimal<<scala/math/BigDecimal#>>]*//*(using BigDecimalIsFractional<<scala/math/Numeric.BigDecimalIsFractional.>>)*//* : BigDecimal<<scala/math/BigDecimal#>>*/
1724+
|""".stripMargin
1725+
)
16901726
}

project/Build.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,9 @@ object Build {
134134
val mimaPreviousDottyVersion = "3.8.0-RC1" // temporary until 3.8.0 is released
135135

136136
/** Version of Scala CLI to download */
137-
val scalaCliLauncherVersion = "1.10.1"
137+
val scalaCliLauncherVersion = "1.11.0"
138138
/** Version of Coursier to download for initializing the local maven repo of Scala command */
139-
val coursierJarVersion = "2.1.25-M19"
139+
val coursierJarVersion = "2.1.25-M21"
140140

141141
object CompatMode {
142142
final val BinaryCompatible = 0

tests/neg/overload_repeated.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,11 @@ object Test {
22
def bar1(x: Any) = 1
33
def bar1(x: String*) = 2
44

5+
// (b) isn't as good as (a) because of ints
6+
// (a) isn't as good as (b) because T in Class[? <: T] may not subtype of Number
7+
def bar2[T](a: Class[? <: T]): Int = 1 // (a)
8+
def bar2[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b)
9+
510
assert(bar1("") == 1) // error: ambiguous in Scala 2 and Scala 3
11+
assert(bar2(classOf[Integer]) == 1) // error: ambiguous in Scala 2 and Scala 3
612
}

0 commit comments

Comments
 (0)