@@ -205,54 +205,63 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
205
205
! owner.isEmptyPackage || ctx.owner.enclosingPackageClass.isEmptyPackage
206
206
}
207
207
208
- import BindingPrec .*
209
- type Result = (Type , BindingPrec )
210
- val NoResult = (NoType , NothingBound )
211
-
212
208
/** 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.
215
213
* @param prevCtx The context of the previous denotation,
216
214
* or else `NoContext` if nothing was found yet.
217
215
*/
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 . *
220
218
221
219
/** Check that any previously found result from an inner context
222
220
* 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
224
223
* @param scala2pkg Special mode where we check members of the same package, but defined
225
224
* in different compilation units under Scala2. If set, and the
226
225
* previous and new contexts do not have the same scope, we select
227
226
* the previous (inner) definition. This models what scalac does.
228
227
*/
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 =
231
229
if ! previous.exists || TypeComparer .isSameRef(previous, found) then
232
230
found
233
231
else if (prevCtx.scope eq ctx.scope)
234
232
&& (newPrec == Definition || newPrec == NamedImport && prevPrec == WildImport )
235
233
then
236
234
// special cases: definitions beat imports, and named imports beat
237
235
// 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
239
243
else
240
244
if ! scala2pkg && ! previous.isError && ! found.isError then
241
245
fail(AmbiguousReference (name, newPrec, prevPrec, prevCtx))
242
246
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
243
253
* `WildImport`.
244
254
* - Find a posssibly shadowing reference in an outer context.
245
255
* If the result is the same as `previous`, check that it is new or
246
256
* shadowed. This order of checking is necessary since an outer package-level
247
257
* definition might trump two conflicting inner imports, so no error should be
248
258
* 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)
251
261
* @param prevCtx the context in which the reference was found
252
262
* @param using_Context the outer context of `precCtx`
253
263
*/
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 =
256
265
257
266
def addAltImport (altImp : TermRef ) =
258
267
if ! TypeComparer .isSameRef(previous, altImp)
@@ -267,20 +276,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
267
276
if prevPrec == WildImport then
268
277
// Discard all previously found references and continue with `altImp`
269
278
altImports.clear()
270
- checkImportAlternatives(( altImp, NamedImport ) , ctx)(using ctx.outer)
279
+ checkImportAlternatives(altImp, NamedImport , ctx)(using ctx.outer)
271
280
else
272
281
addAltImport(altImp)
273
- checkImportAlternatives(prevResult , prevCtx)(using ctx.outer)
282
+ checkImportAlternatives(previous, prevPrec , prevCtx)(using ctx.outer)
274
283
case _ =>
275
284
if prevPrec == WildImport then
276
285
wildImportRef(curImport) match
277
286
case altImp : TermRef => addAltImport(altImp)
278
287
case _ =>
279
- checkImportAlternatives(prevResult , prevCtx)(using ctx.outer)
288
+ checkImportAlternatives(previous, prevPrec , prevCtx)(using ctx.outer)
280
289
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
284
293
end checkImportAlternatives
285
294
286
295
def selection (imp : ImportInfo , name : Name , checkBounds : Boolean ): Type =
@@ -367,10 +376,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
367
376
! noImports &&
368
377
(prevPrec.ordinal < prec.ordinal || prevPrec == prec && (prevCtx.scope eq ctx.scope))
369
378
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
372
381
else {
373
- var result : Result = NoResult
382
+ var result : Type = NoType
374
383
val curOwner = ctx.owner
375
384
376
385
/** Is curOwner a package object that should be skipped?
@@ -469,36 +478,37 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
469
478
effectiveOwner.thisType.select(name, defDenot)
470
479
}
471
480
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
473
482
found match
474
483
case found : NamedType
475
484
if curOwner.isClass && isInherited(found.denot) && ! ctx.compilationUnit.isJava =>
476
485
checkNoOuterDefs(found.denot, ctx, ctx)
477
486
case _ =>
478
487
else
479
488
if migrateTo3 && ! foundUnderScala2.exists then
480
- foundUnderScala2 = checkNewOrShadowed(( found, Definition ) , scala2pkg = true )._1
489
+ foundUnderScala2 = checkNewOrShadowed(found, Definition , scala2pkg = true )
481
490
if (defDenot.symbol.is(Package ))
482
- result = checkNewOrShadowed(( previous orElse found, PackageClause ) )
491
+ result = checkNewOrShadowed(previous orElse found, PackageClause )
483
492
else if (prevPrec.ordinal < PackageClause .ordinal)
484
- result = findRefRecur(( found, PackageClause ) , ctx)(using ctx.outer)
493
+ result = findRefRecur(found, PackageClause , ctx)(using ctx.outer)
485
494
}
486
495
487
- if result._1. exists then result
496
+ if result.exists then result
488
497
else { // find import
489
498
val outer = ctx.outer
490
499
val curImport = ctx.importInfo
500
+ def updateUnimported () =
501
+ if (curImport.nn.unimported ne NoSymbol ) unimported += curImport.nn.unimported
491
502
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)
496
506
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 )
500
510
if (wildImp.exists)
501
- checkImportAlternatives(( wildImp, WildImport ) , ctx)(using outer)
511
+ checkImportAlternatives(wildImp, WildImport , ctx)(using outer)
502
512
else {
503
513
updateUnimported()
504
514
loop(ctx)(using outer)
@@ -517,8 +527,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
517
527
loop(NoContext )
518
528
}
519
529
520
- val (foundRef, foundPrec) = findRefRecur(NoResult , NoContext )
521
- foundRef
530
+ findRefRecur(NoType , BindingPrec .NothingBound , NoContext )
522
531
}
523
532
524
533
/** If `tree`'s type is a `TermRef` identified by flow typing to be non-null, then
0 commit comments