Skip to content

Commit 2d2b2ad

Browse files
committed
Without boxing, just rerun that a named import is behind a wildcard one
1 parent 08d592f commit 2d2b2ad

File tree

1 file changed

+45
-42
lines changed

1 file changed

+45
-42
lines changed

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

+45-42
Original file line numberDiff line numberDiff line change
@@ -241,40 +241,44 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
241241
!owner.isEmptyPackage || ctx.owner.enclosingPackageClass.isEmptyPackage
242242
}
243243

244-
import BindingPrec.*
245-
type Result = (Type, BindingPrec)
246-
val NoResult = (NoType, NothingBound)
247-
248244
/** Find the denotation of enclosing `name` in given context `ctx`.
249-
* @param prevResult A type that was found in a more deeply nested scope,
250-
* and its precedence, or NoResult if nothing was found yet.
245+
* @param previous A denotation that was found in a more deeply nested scope,
246+
* or else `NoDenotation` if nothing was found yet.
247+
* @param prevPrec The binding precedence of the previous denotation,
248+
* or else `nothingBound` if nothing was found yet.
251249
* @param prevCtx The context of the previous denotation,
252250
* or else `NoContext` if nothing was found yet.
253251
*/
254-
def findRefRecur(prevResult: Result, prevCtx: Context)(using Context): Result = {
255-
val (previous, prevPrec) = prevResult
252+
def findRefRecur(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = {
253+
import BindingPrec.*
256254

257255
/** Check that any previously found result from an inner context
258256
* does properly shadow the new one from an outer context.
259-
* @param newResult The newly found type and its precedence.
257+
* @param found The newly found result
258+
* @param newPrec Its precedence
260259
* @param scala2pkg Special mode where we check members of the same package, but defined
261260
* in different compilation units under Scala2. If set, and the
262261
* previous and new contexts do not have the same scope, we select
263262
* the previous (inner) definition. This models what scalac does.
264263
*/
265-
def checkNewOrShadowed(newResult: Result, scala2pkg: Boolean = false)(using Context): Result =
266-
val (found, newPrec) = newResult
264+
def checkNewOrShadowed(found: Type, newPrec: BindingPrec, scala2pkg: Boolean = false)(using Context): Type =
267265
if !previous.exists || TypeComparer.isSameRef(previous, found) then
268-
newResult
266+
found
269267
else if (prevCtx.scope eq ctx.scope) && newPrec.beats(prevPrec) then
270268
// special cases: definitions beat imports, and named imports beat
271269
// wildcard imports, provided both are in contexts with same scope
272-
newResult
270+
found
271+
else if newPrec == WildImport && ctx.outersIterator.exists: ctx =>
272+
ctx.isImportContext && namedImportRef(ctx.importInfo.uncheckedNN).exists
273+
then
274+
// Don't let two ambiguous wildcard imports rule over
275+
// a winning named import. See pos/i18529.
276+
found
273277
else
274278
if !scala2pkg && !previous.isError && !found.isError then
275279
fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx,
276280
isExtension = previous.termSymbol.is(ExtensionMethod) && found.termSymbol.is(ExtensionMethod)))
277-
prevResult
281+
previous
278282

279283
/** Assemble and check alternatives to an imported reference. This implies:
280284
* - If we expand an extension method (i.e. altImports != null),
@@ -287,13 +291,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
287291
* shadowed. This order of checking is necessary since an outer package-level
288292
* definition might trump two conflicting inner imports, so no error should be
289293
* issued in that case. See i7876.scala.
290-
* @param prevResult the previously found reference (which is an import), and
291-
* the precedence of the reference (either NamedImport or WildImport)
294+
* @param previous the previously found reference (which is an import)
295+
* @param prevPrec the precedence of the reference (either NamedImport or WildImport)
292296
* @param prevCtx the context in which the reference was found
293297
* @param using_Context the outer context of `precCtx`
294298
*/
295-
def checkImportAlternatives(prevResult: Result, prevCtx: Context)(using Context): Result =
296-
val (previous, prevPrec) = prevResult
299+
def checkImportAlternatives(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type =
297300

298301
def addAltImport(altImp: TermRef) =
299302
if !TypeComparer.isSameRef(previous, altImp)
@@ -308,20 +311,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
308311
if prevPrec == WildImport then
309312
// Discard all previously found references and continue with `altImp`
310313
altImports.clear()
311-
checkImportAlternatives((altImp, NamedImport), ctx)(using ctx.outer)
314+
checkImportAlternatives(altImp, NamedImport, ctx)(using ctx.outer)
312315
else
313316
addAltImport(altImp)
314-
checkImportAlternatives(prevResult, prevCtx)(using ctx.outer)
317+
checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer)
315318
case _ =>
316319
if prevPrec == WildImport then
317320
wildImportRef(curImport) match
318321
case altImp: TermRef => addAltImport(altImp)
319322
case _ =>
320-
checkImportAlternatives(prevResult, prevCtx)(using ctx.outer)
323+
checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer)
321324
else
322-
val foundResult = findRefRecur(prevResult, prevCtx)
323-
if foundResult._1 eq previous then checkNewOrShadowed(foundResult)(using prevCtx)
324-
else foundResult
325+
val found = findRefRecur(previous, prevPrec, prevCtx)
326+
if found eq previous then checkNewOrShadowed(found, prevPrec)(using prevCtx)
327+
else found
325328
end checkImportAlternatives
326329

327330
def selection(imp: ImportInfo, name: Name, checkBounds: Boolean): Type =
@@ -411,10 +414,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
411414
!noImports &&
412415
(prevPrec.ordinal < prec.ordinal || prevPrec == prec && (prevCtx.scope eq ctx.scope))
413416

414-
@tailrec def loop(lastCtx: Context)(using Context): Result =
415-
if (ctx.scope eq EmptyScope) prevResult
417+
@tailrec def loop(lastCtx: Context)(using Context): Type =
418+
if (ctx.scope eq EmptyScope) previous
416419
else {
417-
var result: Result = NoResult
420+
var result: Type = NoType
418421
val curOwner = ctx.owner
419422

420423
/** Is curOwner a package object that should be skipped?
@@ -513,36 +516,37 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
513516
effectiveOwner.thisType.select(name, defDenot).makePackageObjPrefixExplicit
514517
}
515518
if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then
516-
result = checkNewOrShadowed((found, Definition)) // no need to go further out, we found highest prec entry
519+
result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry
517520
found match
518521
case found: NamedType
519522
if curOwner.isClass && isInherited(found.denot) && !ctx.compilationUnit.isJava =>
520523
checkNoOuterDefs(found.denot, ctx, ctx)
521524
case _ =>
522525
else
523526
if migrateTo3 && !foundUnderScala2.exists then
524-
foundUnderScala2 = checkNewOrShadowed((found, Definition), scala2pkg = true)._1
527+
foundUnderScala2 = checkNewOrShadowed(found, Definition, scala2pkg = true)
525528
if (defDenot.symbol.is(Package))
526-
result = checkNewOrShadowed((previous orElse found, PackageClause))
529+
result = checkNewOrShadowed(previous orElse found, PackageClause)
527530
else if (prevPrec.ordinal < PackageClause.ordinal)
528-
result = findRefRecur((found, PackageClause), ctx)(using ctx.outer)
531+
result = findRefRecur(found, PackageClause, ctx)(using ctx.outer)
529532
}
530533

531-
if result._1.exists then result
534+
if result.exists then result
532535
else { // find import
533536
val outer = ctx.outer
534537
val curImport = ctx.importInfo
538+
def updateUnimported() =
539+
if (curImport.nn.unimported ne NoSymbol) unimported += curImport.nn.unimported
535540
if (curOwner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
536-
prevResult // no more conflicts possible in this case
537-
else if (isPossibleImport(NamedImport) && curImport != null && (curImport ne outer.importInfo)) {
538-
def updateUnimported() = if curImport.unimported ne NoSymbol then unimported += curImport.unimported
539-
val namedImp = namedImportRef(curImport)
541+
previous // no more conflicts possible in this case
542+
else if (isPossibleImport(NamedImport) && (curImport nen outer.importInfo)) {
543+
val namedImp = namedImportRef(curImport.uncheckedNN)
540544
if (namedImp.exists)
541-
checkImportAlternatives((namedImp, NamedImport), ctx)(using outer)
542-
else if (isPossibleImport(WildImport) && !curImport.importSym.isCompleting) {
543-
val wildImp = wildImportRef(curImport)
545+
checkImportAlternatives(namedImp, NamedImport, ctx)(using outer)
546+
else if (isPossibleImport(WildImport) && !curImport.nn.importSym.isCompleting) {
547+
val wildImp = wildImportRef(curImport.uncheckedNN)
544548
if (wildImp.exists)
545-
checkImportAlternatives((wildImp, WildImport), ctx)(using outer)
549+
checkImportAlternatives(wildImp, WildImport, ctx)(using outer)
546550
else {
547551
updateUnimported()
548552
loop(ctx)(using outer)
@@ -561,8 +565,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
561565
loop(NoContext)
562566
}
563567

564-
val (foundRef, foundPrec) = findRefRecur(NoResult, NoContext)
565-
foundRef
568+
findRefRecur(NoType, BindingPrec.NothingBound, NoContext)
566569
}
567570

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

0 commit comments

Comments
 (0)