Skip to content

Commit 062b413

Browse files
committed
Refactoring of findRef
Three goals: 1. Fix crasher in compileStdLib by saving and restoring foundUnderScala2 analogous to iportedFromRoot. 2. Make behavior the same as scalac under Scala2 mode - ListBuffer behaved differently before. 3. Make findRef faster by making it tail-recursive as long as nothing was found.
1 parent fcf3bcd commit 062b413

File tree

1 file changed

+83
-58
lines changed

1 file changed

+83
-58
lines changed

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

Lines changed: 83 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
7777
* in dotty (because dotty conforms to spec section 2
7878
* wrt to package member resolution but scalac doe not).
7979
*/
80-
private var foundUnderScala2: Type = _
80+
private var foundUnderScala2: Type = NoType
8181

8282
def newLikeThis: Typer = new Typer
8383

@@ -140,14 +140,20 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
140140
* imported by <tree>
141141
* or defined in <symbol>
142142
*/
143-
def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
143+
def bindingString(prec: Int, whereFound: Context, qualifier: String = "")(implicit ctx: Context) =
144144
if (prec == wildImport || prec == namedImport) ex"imported$qualifier by ${whereFound.importInfo}"
145145
else ex"defined$qualifier in ${whereFound.owner}"
146146

147147
/** Check that any previously found result from an inner context
148148
* does properly shadow the new one from an outer context.
149+
* @param found The newly found result
150+
* @param newPrec Its precedence
151+
* @param scala2pkg Special mode where we check members of the same package, but defined
152+
* in different compilation units under Scala2. If set, and the
153+
* previous and new contexts do not have the same scope, we select
154+
* the previous (inner) definition. This models what scalac does.
149155
*/
150-
def checkNewOrShadowed(found: Type, newPrec: Int): Type =
156+
def checkNewOrShadowed(found: Type, newPrec: Int, scala2pkg: Boolean = false)(implicit ctx: Context): Type =
151157
if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found
152158
else if ((prevCtx.scope eq ctx.scope) &&
153159
(newPrec == definition ||
@@ -157,7 +163,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
157163
found
158164
}
159165
else {
160-
if (!previous.isError && !found.isError) {
166+
if (!scala2pkg && !previous.isError && !found.isError) {
161167
error(
162168
ex"""reference to $name is ambiguous;
163169
|it is both ${bindingString(newPrec, ctx, "")}
@@ -170,7 +176,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
170176
/** The type representing a named import with enclosing name when imported
171177
* from given `site` and `selectors`.
172178
*/
173-
def namedImportRef(site: Type, selectors: List[untpd.Tree]): Type = {
179+
def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = {
174180
def checkUnambiguous(found: Type) = {
175181
val other = namedImportRef(site, selectors.tail)
176182
if (other.exists && found.exists && (found != other))
@@ -197,7 +203,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
197203
/** The type representing a wildcard import with enclosing name when imported
198204
* from given import info
199205
*/
200-
def wildImportRef(imp: ImportInfo): Type = {
206+
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = {
201207
if (imp.isWildcardImport) {
202208
val pre = imp.site
203209
if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) {
@@ -211,58 +217,71 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
211217
/** Is (some alternative of) the given predenotation `denot`
212218
* defined in current compilation unit?
213219
*/
214-
def isDefinedInCurrentUnit(denot: Denotation): Boolean = denot match {
220+
def isDefinedInCurrentUnit(denot: Denotation)(implicit ctx: Context): Boolean = denot match {
215221
case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2)
216222
case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source.file
217223
}
218224

219225
/** Is `denot` the denotation of a self symbol? */
220-
def isSelfDenot(denot: Denotation) = denot match {
226+
def isSelfDenot(denot: Denotation)(implicit ctx: Context) = denot match {
221227
case denot: SymDenotation => denot is SelfName
222228
case _ => false
223229
}
224230

225-
// begin findRef
226-
if (ctx.scope == null) previous
227-
else {
228-
val outer = ctx.outer
229-
if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
230-
val defDenot = ctx.denotNamed(name)
231-
if (qualifies(defDenot)) {
232-
val curOwner = ctx.owner
233-
val found =
234-
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
235-
else curOwner.thisType.select(name, defDenot)
236-
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot))
237-
return checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
238-
else {
239-
if (ctx.scala2Mode)
240-
foundUnderScala2 = checkNewOrShadowed(found, definition)
241-
if (defDenot.symbol is Package)
242-
return checkNewOrShadowed(previous orElse found, packageClause)
243-
else if (prevPrec < packageClause)
244-
return findRef(found, packageClause, ctx)(outer)
231+
/** Would import of kind `prec` be not shadowed by a nested higher-precedence definition? */
232+
def isPossibleImport(prec: Int)(implicit ctx: Context) =
233+
prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope)
234+
235+
@tailrec def loop(implicit ctx: Context): Type = {
236+
if (ctx.scope == null) previous
237+
else {
238+
val outer = ctx.outer
239+
var result: Type = NoType
240+
241+
// find definition
242+
if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
243+
val defDenot = ctx.denotNamed(name)
244+
if (qualifies(defDenot)) {
245+
val curOwner = ctx.owner
246+
val found =
247+
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
248+
else curOwner.thisType.select(name, defDenot)
249+
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot))
250+
result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
251+
else {
252+
if (ctx.scala2Mode && !foundUnderScala2.exists)
253+
foundUnderScala2 = checkNewOrShadowed(found, definition, scala2pkg = true)
254+
if (defDenot.symbol is Package)
255+
result = checkNewOrShadowed(previous orElse found, packageClause)
256+
else if (prevPrec < packageClause)
257+
result = findRef(found, packageClause, ctx)(outer)
258+
}
245259
}
246260
}
247-
}
248-
val curImport = ctx.importInfo
249-
if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
250-
return previous // no more conflicts possible in this case
251-
// would import of kind `prec` be not shadowed by a nested higher-precedence definition?
252-
def isPossibleImport(prec: Int) =
253-
prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope)
254-
if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) {
255-
val namedImp = namedImportRef(curImport.site, curImport.selectors)
256-
if (namedImp.exists)
257-
return findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
258-
if (isPossibleImport(wildImport)) {
259-
val wildImp = wildImportRef(curImport)
260-
if (wildImp.exists)
261-
return findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
261+
262+
if (result.exists) result
263+
else { // find import
264+
val curImport = ctx.importInfo
265+
if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
266+
previous // no more conflicts possible in this case
267+
else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) {
268+
val namedImp = namedImportRef(curImport.site, curImport.selectors)
269+
if (namedImp.exists)
270+
findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
271+
else if (isPossibleImport(wildImport)) {
272+
val wildImp = wildImportRef(curImport)
273+
if (wildImp.exists)
274+
findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
275+
else loop(outer)
276+
}
277+
else loop(outer)
278+
}
279+
else loop(outer)
262280
}
263281
}
264-
findRef(previous, prevPrec, prevCtx)(outer)
265282
}
283+
284+
loop
266285
}
267286

268287
// begin typedIdent
@@ -275,21 +294,27 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
275294
return typed(desugar.patternVar(tree), pt)
276295
}
277296

278-
val saved = importedFromRoot
279-
importedFromRoot = Set.empty
280-
281-
foundUnderScala2 = NoType
282-
283-
var rawType =
284-
try findRef(NoType, BindingPrec.nothingBound, NoContext)
285-
finally importedFromRoot = saved
286297

287-
if (foundUnderScala2.exists && (foundUnderScala2 ne rawType)) {
288-
ctx.migrationWarning(
289-
ex"""Name resolution will change.
290-
| currently selected : $foundUnderScala2
291-
| in the future, without -language:Scala2: $rawType""", tree.pos)
292-
rawType = foundUnderScala2
298+
val rawType = {
299+
val saved1 = importedFromRoot
300+
val saved2 = foundUnderScala2
301+
importedFromRoot = Set.empty
302+
foundUnderScala2 = NoType
303+
try {
304+
var found = findRef(NoType, BindingPrec.nothingBound, NoContext)
305+
if (foundUnderScala2.exists && !(foundUnderScala2 =:= found)) {
306+
ctx.migrationWarning(
307+
ex"""Name resolution will change.
308+
| currently selected : $foundUnderScala2
309+
| in the future, without -language:Scala2: $found""", tree.pos)
310+
found = foundUnderScala2
311+
}
312+
found
313+
}
314+
finally {
315+
importedFromRoot = saved1
316+
foundUnderScala2 = saved2
317+
}
293318
}
294319

295320
val ownType =

0 commit comments

Comments
 (0)