Skip to content

Commit a569fe8

Browse files
dwijnandtgodzik
authored andcommitted
Without boxing, just rerun that a named import is behind a wildcard one
1 parent f9a0723 commit a569fe8

File tree

1 file changed

+49
-40
lines changed

1 file changed

+49
-40
lines changed

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

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -205,54 +205,63 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
205205
!owner.isEmptyPackage || ctx.owner.enclosingPackageClass.isEmptyPackage
206206
}
207207

208-
import BindingPrec.*
209-
type Result = (Type, BindingPrec)
210-
val NoResult = (NoType, NothingBound)
211-
212208
/** Find the denotation of enclosing `name` in given context `ctx`.
213-
* @param prevResult A type that was found in a more deeply nested scope,
214-
* and its precedence, or NoResult if nothing was found yet.
209+
* @param previous A denotation that was found in a more deeply nested scope,
210+
* or else `NoDenotation` if nothing was found yet.
211+
* @param prevPrec The binding precedence of the previous denotation,
212+
* or else `nothingBound` if nothing was found yet.
215213
* @param prevCtx The context of the previous denotation,
216214
* or else `NoContext` if nothing was found yet.
217215
*/
218-
def findRefRecur(prevResult: Result, prevCtx: Context)(using Context): Result = {
219-
val (previous, prevPrec) = prevResult
216+
def findRefRecur(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = {
217+
import BindingPrec.*
220218

221219
/** Check that any previously found result from an inner context
222220
* does properly shadow the new one from an outer context.
223-
* @param newResult The newly found type and its precedence.
221+
* @param found The newly found result
222+
* @param newPrec Its precedence
224223
* @param scala2pkg Special mode where we check members of the same package, but defined
225224
* in different compilation units under Scala2. If set, and the
226225
* previous and new contexts do not have the same scope, we select
227226
* the previous (inner) definition. This models what scalac does.
228227
*/
229-
def checkNewOrShadowed(newResult: Result, scala2pkg: Boolean = false)(using Context): Result =
230-
val (found, newPrec) = newResult
228+
def checkNewOrShadowed(found: Type, newPrec: BindingPrec, scala2pkg: Boolean = false)(using Context): Type =
231229
if !previous.exists || TypeComparer.isSameRef(previous, found) then
232230
found
233231
else if (prevCtx.scope eq ctx.scope)
234232
&& (newPrec == Definition || newPrec == NamedImport && prevPrec == WildImport)
235233
then
236234
// special cases: definitions beat imports, and named imports beat
237235
// wildcard imports, provided both are in contexts with same scope
238-
newResult
236+
found
237+
else if newPrec == WildImport && ctx.outersIterator.exists: ctx =>
238+
ctx.isImportContext && namedImportRef(ctx.importInfo.uncheckedNN).exists
239+
then
240+
// Don't let two ambiguous wildcard imports rule over
241+
// a winning named import. See pos/i18529.
242+
found
239243
else
240244
if !scala2pkg && !previous.isError && !found.isError then
241245
fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx))
242246
previous
247+
248+
/** Assemble and check alternatives to an imported reference. This implies:
249+
* - If we expand an extension method (i.e. altImports != null),
250+
* search imports on the same level for other possible resolutions of `name`.
251+
* The result and altImports together then contain all possible imported
252+
* references of the highest possible precedence, where `NamedImport` beats
243253
* `WildImport`.
244254
* - Find a posssibly shadowing reference in an outer context.
245255
* If the result is the same as `previous`, check that it is new or
246256
* shadowed. This order of checking is necessary since an outer package-level
247257
* definition might trump two conflicting inner imports, so no error should be
248258
* issued in that case. See i7876.scala.
249-
* @param prevResult the previously found reference (which is an import), and
250-
* the precedence of the reference (either NamedImport or WildImport)
259+
* @param previous the previously found reference (which is an import)
260+
* @param prevPrec the precedence of the reference (either NamedImport or WildImport)
251261
* @param prevCtx the context in which the reference was found
252262
* @param using_Context the outer context of `precCtx`
253263
*/
254-
def checkImportAlternatives(prevResult: Result, prevCtx: Context)(using Context): Result =
255-
val (previous, prevPrec) = prevResult
264+
def checkImportAlternatives(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type =
256265

257266
def addAltImport(altImp: TermRef) =
258267
if !TypeComparer.isSameRef(previous, altImp)
@@ -267,20 +276,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
267276
if prevPrec == WildImport then
268277
// Discard all previously found references and continue with `altImp`
269278
altImports.clear()
270-
checkImportAlternatives((altImp, NamedImport), ctx)(using ctx.outer)
279+
checkImportAlternatives(altImp, NamedImport, ctx)(using ctx.outer)
271280
else
272281
addAltImport(altImp)
273-
checkImportAlternatives(prevResult, prevCtx)(using ctx.outer)
282+
checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer)
274283
case _ =>
275284
if prevPrec == WildImport then
276285
wildImportRef(curImport) match
277286
case altImp: TermRef => addAltImport(altImp)
278287
case _ =>
279-
checkImportAlternatives(prevResult, prevCtx)(using ctx.outer)
288+
checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer)
280289
else
281-
val foundResult = findRefRecur(prevResult, prevCtx)
282-
if foundResult._1 eq previous then checkNewOrShadowed(foundResult)(using prevCtx)
283-
else foundResult
290+
val found = findRefRecur(previous, prevPrec, prevCtx)
291+
if found eq previous then checkNewOrShadowed(found, prevPrec)(using prevCtx)
292+
else found
284293
end checkImportAlternatives
285294

286295
def selection(imp: ImportInfo, name: Name, checkBounds: Boolean): Type =
@@ -367,10 +376,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
367376
!noImports &&
368377
(prevPrec.ordinal < prec.ordinal || prevPrec == prec && (prevCtx.scope eq ctx.scope))
369378

370-
@tailrec def loop(lastCtx: Context)(using Context): Result =
371-
if (ctx.scope eq EmptyScope) prevResult
379+
@tailrec def loop(lastCtx: Context)(using Context): Type =
380+
if (ctx.scope eq EmptyScope) previous
372381
else {
373-
var result: Result = NoResult
382+
var result: Type = NoType
374383
val curOwner = ctx.owner
375384

376385
/** Is curOwner a package object that should be skipped?
@@ -469,36 +478,37 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
469478
effectiveOwner.thisType.select(name, defDenot)
470479
}
471480
if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then
472-
result = checkNewOrShadowed((found, Definition)) // no need to go further out, we found highest prec entry
481+
result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry
473482
found match
474483
case found: NamedType
475484
if curOwner.isClass && isInherited(found.denot) && !ctx.compilationUnit.isJava =>
476485
checkNoOuterDefs(found.denot, ctx, ctx)
477486
case _ =>
478487
else
479488
if migrateTo3 && !foundUnderScala2.exists then
480-
foundUnderScala2 = checkNewOrShadowed((found, Definition), scala2pkg = true)._1
489+
foundUnderScala2 = checkNewOrShadowed(found, Definition, scala2pkg = true)
481490
if (defDenot.symbol.is(Package))
482-
result = checkNewOrShadowed((previous orElse found, PackageClause))
491+
result = checkNewOrShadowed(previous orElse found, PackageClause)
483492
else if (prevPrec.ordinal < PackageClause.ordinal)
484-
result = findRefRecur((found, PackageClause), ctx)(using ctx.outer)
493+
result = findRefRecur(found, PackageClause, ctx)(using ctx.outer)
485494
}
486495

487-
if result._1.exists then result
496+
if result.exists then result
488497
else { // find import
489498
val outer = ctx.outer
490499
val curImport = ctx.importInfo
500+
def updateUnimported() =
501+
if (curImport.nn.unimported ne NoSymbol) unimported += curImport.nn.unimported
491502
if (curOwner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
492-
prevResult // no more conflicts possible in this case
493-
else if (isPossibleImport(NamedImport) && curImport != null && (curImport ne outer.importInfo)) {
494-
def updateUnimported() = if curImport.unimported ne NoSymbol then unimported += curImport.unimported
495-
val namedImp = namedImportRef(curImport)
503+
previous // no more conflicts possible in this case
504+
else if (isPossibleImport(NamedImport) && (curImport nen outer.importInfo)) {
505+
val namedImp = namedImportRef(curImport.uncheckedNN)
496506
if (namedImp.exists)
497-
checkImportAlternatives((namedImp, NamedImport), ctx)(using outer)
498-
else if (isPossibleImport(WildImport) && !curImport.importSym.isCompleting) {
499-
val wildImp = wildImportRef(curImport)
507+
checkImportAlternatives(namedImp, NamedImport, ctx)(using outer)
508+
else if (isPossibleImport(WildImport) && !curImport.nn.importSym.isCompleting) {
509+
val wildImp = wildImportRef(curImport.uncheckedNN)
500510
if (wildImp.exists)
501-
checkImportAlternatives((wildImp, WildImport), ctx)(using outer)
511+
checkImportAlternatives(wildImp, WildImport, ctx)(using outer)
502512
else {
503513
updateUnimported()
504514
loop(ctx)(using outer)
@@ -517,8 +527,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
517527
loop(NoContext)
518528
}
519529

520-
val (foundRef, foundPrec) = findRefRecur(NoResult, NoContext)
521-
foundRef
530+
findRefRecur(NoType, BindingPrec.NothingBound, NoContext)
522531
}
523532

524533
/** If `tree`'s type is a `TermRef` identified by flow typing to be non-null, then

0 commit comments

Comments
 (0)