Skip to content

Commit d49591c

Browse files
committed
Remove premature, brittle caching in lint
1 parent 107a2f1 commit d49591c

File tree

2 files changed

+35
-71
lines changed

2 files changed

+35
-71
lines changed

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

+1-71
Original file line numberDiff line numberDiff line change
@@ -215,15 +215,6 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
215215
refInfos.register(tree)
216216
tree
217217

218-
override def prepareForTemplate(tree: Template)(using Context): Context =
219-
ctx.fresh.setProperty(resolvedKey, Resolved())
220-
221-
override def prepareForPackageDef(tree: PackageDef)(using Context): Context =
222-
ctx.fresh.setProperty(resolvedKey, Resolved())
223-
224-
override def prepareForStats(trees: List[Tree])(using Context): Context =
225-
ctx.fresh.setProperty(resolvedKey, Resolved())
226-
227218
override def transformOther(tree: Tree)(using Context): tree.type =
228219
tree match
229220
case imp: Import =>
@@ -289,7 +280,6 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
289280
case ByNameTypeTree(result) =>
290281
transformAllDeep(result)
291282
//case _: InferredTypeTree => // do nothing
292-
//case _: Export => // nothing to do
293283
//case _ if tree.isType =>
294284
case _ =>
295285
tree
@@ -350,15 +340,6 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
350340
&& ctxsym.thisType.baseClasses.contains(sym.owner)
351341
&& ctxsym.thisType.member(sym.name).hasAltWith(d => d.containsSym(sym) && !name.exists(_ != d.name))
352342

353-
// Attempt to cache a result at the given context. Not all contexts bear a cache, including NoContext.
354-
// If there is already any result for the name and prefix, do nothing.
355-
def addCached(where: Context, result: Precedence): Unit =
356-
if where.moreProperties ne null then
357-
where.property(resolvedKey) match
358-
case Some(resolved) =>
359-
resolved.record(sym, name, prefix, result)
360-
case none =>
361-
362343
// Avoid spurious NoSymbol and also primary ctors which are never warned about.
363344
// Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
364345
if !sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
@@ -367,12 +348,10 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
367348
// If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
368349
val isLocal = sym.isLocalToBlock
369350
var candidate: Context = NoContext
370-
var cachePoint: Context = NoContext // last context with Resolved cache
371351
var importer: ImportSelector | Null = null // non-null for import context
372352
var precedence = NoPrecedence // of current resolution
373353
var enclosed = false // true if sym is owner of an enclosing context
374354
var done = false
375-
var cached = false
376355
val ctxs = ctx.outersIterator
377356
while !done && ctxs.hasNext do
378357
val cur = ctxs.next()
@@ -382,24 +361,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
382361
if cur.owner eq sym.owner then
383362
done = true // for local def, just checking that it is not enclosing
384363
else
385-
val cachedPrecedence =
386-
cur.property(resolvedKey) match
387-
case Some(resolved) =>
388-
// conservative, cache must be nested below the result context
389-
if precedence.isNone then
390-
cachePoint = cur // no result yet, and future result could be cached here
391-
resolved.hasRecord(sym, name, prefix)
392-
case none => NoPrecedence
393-
cached = !cachedPrecedence.isNone
394-
if cached then
395-
// if prefer cached precedence, then discard previous result
396-
if precedence.weakerThan(cachedPrecedence) then
397-
candidate = NoContext
398-
importer = null
399-
cachePoint = cur // actual cache context
400-
precedence = cachedPrecedence // actual cached precedence
401-
done = true
402-
else if cur.isImportContext then
364+
if cur.isImportContext then
403365
val sel = matchingSelector(cur.importInfo.nn)
404366
if sel != null then
405367
if cur.importInfo.nn.isRootImport then
@@ -433,13 +395,6 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
433395
refInfos.refs.addOne(sym)
434396
if candidate != NoContext && candidate.isImportContext && importer != null then
435397
refInfos.sels.put(importer, ())
436-
// possibly record that we have performed this look-up
437-
// if no result was found, take it as Definition (local or rooted head of fully qualified path)
438-
val adjusted = if precedence.isNone then Definition else precedence
439-
if !cached && (cachePoint ne NoContext) then
440-
addCached(cachePoint, adjusted)
441-
if cachePoint ne ctx then
442-
addCached(ctx, adjusted) // at this ctx, since cachePoint may be far up the outer chain
443398
end resolveUsage
444399
end CheckUnused
445400

@@ -451,15 +406,8 @@ object CheckUnused:
451406

452407
val refInfosKey = Property.StickyKey[RefInfos]
453408

454-
val resolvedKey = Property.Key[Resolved]
455-
456409
inline def refInfos(using Context): RefInfos = ctx.property(refInfosKey).get
457410

458-
inline def resolved(using Context): Resolved =
459-
ctx.property(resolvedKey) match
460-
case Some(res) => res
461-
case _ => throw new MatchError("no Resolved for context")
462-
463411
/** Attachment holding the name of an Ident as written by the user. */
464412
val OriginalName = Property.StickyKey[Name]
465413

@@ -515,24 +463,6 @@ object CheckUnused:
515463
var inliners = 0 // depth of inline def (not inlined yet)
516464
end RefInfos
517465

518-
// Symbols already resolved in the given Context (with name and prefix of lookup).
519-
class Resolved:
520-
import PrecedenceLevels.*
521-
private val seen = mutable.Map.empty[Symbol, List[(Name, Type, Precedence)]].withDefaultValue(Nil)
522-
// if a result has been recorded, return it; otherwise, NoPrecedence.
523-
def hasRecord(symbol: Symbol, name: Name, prefix: Type)(using Context): Precedence =
524-
seen(symbol).find((n, p, _) => n == name && p =:= prefix) match
525-
case Some((_, _, r)) => r
526-
case none => NoPrecedence
527-
// "record" the look-up result, if there is not already a result for the name and prefix.
528-
def record(symbol: Symbol, name: Name, prefix: Type, result: Precedence)(using Context): Unit =
529-
require(NoPrecedence.weakerThan(result))
530-
seen.updateWith(symbol):
531-
case svs @ Some(vs) =>
532-
if vs.exists((n, p, _) => n == name && p =:= prefix) then svs
533-
else Some((name, prefix, result) :: vs)
534-
case none => Some((name, prefix, result) :: Nil)
535-
536466
// Names are resolved by definitions and imports, which have four precedence levels:
537467
object PrecedenceLevels:
538468
opaque type Precedence = Int

tests/warn/i22971.scala

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//> using options -Wunused:imports
2+
3+
package p:
4+
5+
trait Base
6+
class Class extends Base
7+
8+
abstract class Entity[T: GetType]
9+
10+
class Thing extends Entity[Class]
11+
12+
trait GetType[T]
13+
14+
object GetType {
15+
//implicit object GetTypeClass extends GetType[Class]
16+
implicit val GetTypeClass: GetType[Class] = new GetType[Class] {}
17+
}
18+
object Main {
19+
def main(args: Array[String]): Unit = {
20+
import GetType.*
21+
val e = GetTypeClass
22+
}
23+
}
24+
25+
package q:
26+
27+
class C:
28+
def f =
29+
import p.*
30+
GetType.GetTypeClass
31+
def g =
32+
import p.GetType.*
33+
GetTypeClass
34+
class D extends p.Entity[p.Class]

0 commit comments

Comments
 (0)