Skip to content

Commit fdc01a8

Browse files
committed
Simplifications
Avoid a notation of "owner" when checking for a language feature. Instead, allow qualified names for language features, e.g. `experimental.dependent".
1 parent 49840c1 commit fdc01a8

File tree

6 files changed

+105
-80
lines changed

6 files changed

+105
-80
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -251,21 +251,27 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
251251
case TypeDefs(_) => true
252252
case _ => isUsingClause(params)
253253

254-
private val languageImportParts = List(nme.language, nme.scala, nme.ROOTPKG)
255-
private val languageImportNested = Set[Name](nme.experimental)
256-
257-
/** Strip the name of any local object in `scala.language` from `path` */
258-
def stripLanguageNested(path: Tree): Tree = path match
259-
case Select(qual, name) if languageImportNested.contains(name) => qual
260-
case _ => path
261-
262-
/** Does this `path` look like a language import? */
263-
def isLanguageImport(path: Tree): Boolean =
264-
def parts(tree: Tree): List[Name] = tree match
265-
case tree: RefTree => tree.name :: parts(tree.qualifier)
266-
case EmptyTree => Nil
267-
case _ => EmptyTermName :: Nil
268-
!path.isEmpty && languageImportParts.startsWith(parts(stripLanguageNested(path)))
254+
/** If `path` looks like a language import, `Some(name)` where name
255+
* is `experimental` if that sub-module is imported, and the empty
256+
* term name otherwise.
257+
*/
258+
def languageImport(path: Tree): Option[TermName] = path match
259+
case Select(p1, nme.experimental) =>
260+
languageImport(p1) match
261+
case Some(EmptyTermName) => Some(nme.experimental)
262+
case _ => None
263+
case p1: RefTree if p1.name == nme.language =>
264+
p1.qualifier match
265+
case EmptyTree => Some(EmptyTermName)
266+
case p2: RefTree if p2.name == nme.scala =>
267+
p2.qualifier match
268+
case EmptyTree => Some(EmptyTermName)
269+
case Ident(nme.ROOTPKG) => Some(EmptyTermName)
270+
case _ => None
271+
case _ => None
272+
case _ => None
273+
274+
def isLanguageImport(path: Tree): Boolean = languageImport(path).isDefined
269275

270276
/** The underlying pattern ignoring any bindings */
271277
def unbind(x: Tree): Tree = unsplice(x) match {

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ import Decorators.{_, given}
99
import util.SrcPos
1010
import SourceVersion._
1111
import reporting.Message
12+
import NameKinds.QualifiedName
1213

1314
object Feature:
1415

15-
private val dependent = "dependent".toTermName
16-
private val namedTypeArguments = "namedTypeArguments".toTermName
17-
private val genericNumberLiterals = "genericNumberLiterals".toTermName
16+
private def experimental(str: String): TermName =
17+
QualifiedName(nme.experimental, str.toTermName)
18+
19+
private val Xdependent = experimental("dependent")
20+
private val XnamedTypeArguments = experimental("namedTypeArguments")
21+
private val XgenericNumberLiterals = experimental("genericNumberLiterals")
22+
private val Xmacros = experimental("macros")
1823

1924
/** Is `feature` enabled by by a command-line setting? The enabling setting is
2025
*
@@ -23,12 +28,8 @@ object Feature:
2328
* where <prefix> is the fully qualified name of `owner`, followed by a ".",
2429
* but subtracting the prefix `scala.language.` at the front.
2530
*/
26-
def enabledBySetting(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
27-
def toPrefix(sym: Symbol): String =
28-
if !sym.exists || sym == defn.LanguageModule.moduleClass then ""
29-
else toPrefix(sym.owner) + sym.name.stripModuleClassSuffix + "."
30-
val prefix = if owner ne NoSymbol then toPrefix(owner) else ""
31-
ctx.base.settings.language.value.contains(prefix + feature)
31+
def enabledBySetting(feature: TermName)(using Context): Boolean =
32+
ctx.base.settings.language.value.contains(feature.toString)
3233

3334
/** Is `feature` enabled by by an import? This is the case if the feature
3435
* is imported by a named import
@@ -39,39 +40,31 @@ object Feature:
3940
*
4041
* import owner.{ feature => _ }
4142
*/
42-
def enabledByImport(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
43-
atPhase(typerPhase) {
44-
ctx.importInfo != null
45-
&& ctx.importInfo.featureImported(feature,
46-
if owner.exists then owner else defn.LanguageModule.moduleClass)
47-
}
43+
def enabledByImport(feature: TermName)(using Context): Boolean =
44+
//atPhase(typerPhase) {
45+
ctx.importInfo != null && ctx.importInfo.featureImported(feature)
46+
//}
4847

4948
/** Is `feature` enabled by either a command line setting or an import?
5049
* @param feature The name of the feature
5150
* @param owner The prefix symbol (nested in `scala.language`) where the
5251
* feature is defined.
5352
*/
54-
def enabled(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
55-
enabledBySetting(feature, owner) || enabledByImport(feature, owner)
53+
def enabled(feature: TermName)(using Context): Boolean =
54+
enabledBySetting(feature) || enabledByImport(feature)
5655

5756
/** Is auto-tupling enabled? */
58-
def autoTuplingEnabled(using Context): Boolean =
59-
!enabled(nme.noAutoTupling)
57+
def autoTuplingEnabled(using Context): Boolean = !enabled(nme.noAutoTupling)
6058

61-
def dynamicsEnabled(using Context): Boolean =
62-
enabled(nme.dynamics)
59+
def dynamicsEnabled(using Context): Boolean = enabled(nme.dynamics)
6360

64-
def dependentEnabled(using Context) =
65-
enabled(dependent, defn.LanguageExperimentalModule.moduleClass)
61+
def dependentEnabled(using Context) = enabled(Xdependent)
6662

67-
def namedTypeArgsEnabled(using Context) =
68-
enabled(namedTypeArguments, defn.LanguageExperimentalModule.moduleClass)
63+
def namedTypeArgsEnabled(using Context) = enabled(XnamedTypeArguments)
6964

70-
def genericNumberLiteralsEnabled(using Context) =
71-
enabled(genericNumberLiterals, defn.LanguageExperimentalModule.moduleClass)
65+
def genericNumberLiteralsEnabled(using Context) = enabled(XgenericNumberLiterals)
7266

73-
def scala2ExperimentalMacroEnabled(using Context) =
74-
enabled("macros".toTermName, defn.LanguageExperimentalModule.moduleClass)
67+
def scala2ExperimentalMacroEnabled(using Context) = enabled(Xmacros)
7568

7669
def sourceVersionSetting(using Context): SourceVersion =
7770
SourceVersion.valueOf(ctx.settings.source.value)

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,19 @@ trait Checking {
736736
/** Check that `path` is a legal prefix for an import clause */
737737
def checkLegalImportPath(path: Tree)(using Context): Unit =
738738
checkLegalImportOrExportPath(path, "import prefix")
739-
val normPath = stripLanguageNested(path)
740-
if isLanguageImport(normPath) then
741-
if normPath.symbol != defn.LanguageModule then
742-
report.error(em"import looks like a language import, but refers to something else: ${normPath.symbol.showLocated}", path.srcPos)
743-
else
744-
if normPath.tpe.classSymbols.contains(defn.LanguageModule.moduleClass) then
745-
report.error(em"no aliases can be used to refer to a language import", path.srcPos)
739+
languageImport(path) match
740+
case Some(prefix) =>
741+
val required =
742+
if prefix == nme.experimental then defn.LanguageExperimentalModule
743+
else defn.LanguageModule
744+
if path.symbol != required then
745+
report.error(em"import looks like a language import, but refers to something else: ${path.symbol.showLocated}", path.srcPos)
746+
case None =>
747+
val foundClasses = path.tpe.classSymbols
748+
if foundClasses.contains(defn.LanguageModule.moduleClass)
749+
|| foundClasses.contains(defn.LanguageExperimentalModule.moduleClass)
750+
then
751+
report.error(em"no aliases can be used to refer to a language import", path.srcPos)
746752

747753
/** Check that `path` is a legal prefix for an export clause */
748754
def checkLegalExportPath(path: Tree, selectors: List[untpd.ImportSelector])(using Context): Unit =

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

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import config.SourceVersion
1313
import StdNames.nme
1414
import printing.Texts.Text
1515
import ProtoTypes.NoViewsAllowed.normalizedCompatible
16+
import NameKinds.QualifiedName
1617
import Decorators._
1718

1819
object ImportInfo {
@@ -45,7 +46,7 @@ object ImportInfo {
4546
}
4647

4748
/** Info relating to an import clause
48-
* @param sym The import symbol defined by the clause
49+
* @param symf A function that computes the import symbol defined by the clause
4950
* @param selectors The selector clauses
5051
* @param qualifier The import qualifier, or EmptyTree for root imports.
5152
* Defined for all explicit imports from ident or select nodes.
@@ -62,7 +63,7 @@ class ImportInfo(symf: Context ?=> Symbol,
6263
case _ => None
6364
}
6465

65-
def sym(using Context): Symbol = {
66+
def importSym(using Context): Symbol = {
6667
if (mySym == null) {
6768
mySym = symf
6869
assert(mySym != null)
@@ -72,7 +73,7 @@ class ImportInfo(symf: Context ?=> Symbol,
7273
private var mySym: Symbol = _
7374

7475
/** The (TermRef) type of the qualifier of the import clause */
75-
def site(using Context): Type = sym.info match {
76+
def site(using Context): Type = importSym.info match {
7677
case ImportType(expr) => expr.tpe
7778
case _ => NoType
7879
}
@@ -185,27 +186,46 @@ class ImportInfo(symf: Context ?=> Symbol,
185186

186187
private var myUnimported: Symbol = _
187188

188-
private var myOwner: Symbol = null
189-
private var myResults: SimpleIdentityMap[TermName, java.lang.Boolean] = SimpleIdentityMap.empty
190-
191-
/** Does this import clause or a preceding import clause import `owner.feature`? */
192-
def featureImported(feature: TermName, owner: Symbol)(using Context): Boolean =
193-
194-
def compute: Boolean =
195-
if isLanguageImport then
196-
val isImportOwner = site.typeSymbol.eq(owner)
197-
if isImportOwner then
198-
if forwardMapping.contains(feature) then return true
199-
if excluded.contains(feature) then return false
200-
var c = ctx.outer
201-
while c.importInfo eq ctx.importInfo do c = c.outer
202-
(c.importInfo != null) && c.importInfo.featureImported(feature, owner)(using c)
203-
204-
if myOwner.ne(owner) || !myResults.contains(feature) then
205-
myOwner = owner
206-
myResults = myResults.updated(feature, compute)
207-
myResults(feature)
208-
end featureImported
189+
private var featureCache: SimpleIdentityMap[TermName, java.lang.Boolean] = SimpleIdentityMap.empty
190+
191+
/** Does this import clause or a preceding import clause enable or disable `feature`?
192+
* @param feature See featureImported for a description
193+
* @return Some(true) if `feature` is imported
194+
* Some(false) if `feature` is excluded
195+
* None if `feature` is not mentioned, or this is not a language import
196+
*/
197+
def mentionsFeature(feature: TermName)(using Context): Option[Boolean] =
198+
def test(prefix: TermName, feature: TermName): Option[Boolean] =
199+
untpd.languageImport(qualifier) match
200+
case Some(`prefix`) =>
201+
if forwardMapping.contains(feature) then Some(true)
202+
else if excluded.contains(feature) then Some(false)
203+
else None
204+
case _ => None
205+
feature match
206+
case QualifiedName(prefix, name) => test(prefix, name)
207+
case _ => test(EmptyTermName, feature)
208+
209+
/** Does this import clause or a preceding import clause enable `feature`?
210+
*
211+
* @param feature a possibly quailified name, e.g.
212+
* strictEquality
213+
* experimental.genericNumberLiterals
214+
*
215+
* An excluded feature such as `strictEquality => _` in a language import
216+
* means that preceding imports are not considered and the feature is not imported.
217+
*/
218+
def featureImported(feature: TermName)(using Context): Boolean =
219+
if !featureCache.contains(feature) then
220+
featureCache = featureCache.updated(feature,
221+
mentionsFeature(feature) match
222+
case Some(bv) => bv
223+
case None =>
224+
var c = ctx.outer
225+
while c.importInfo eq ctx.importInfo do c = c.outer
226+
(c.importInfo != null) && c.importInfo.featureImported(feature)(using c)
227+
)
228+
featureCache(feature)
209229

210230
def toText(printer: Printer): Text = printer.toText(this)
211231
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ trait ImportSuggestions:
122122
.flatMap(sym => rootsIn(sym.termRef))
123123
val imported =
124124
if ctx.importInfo eq ctx.outer.importInfo then Nil
125-
else ctx.importInfo.sym.info match
125+
else ctx.importInfo.importSym.info match
126126
case ImportType(expr) => rootsOnPath(expr.tpe)
127127
case _ => Nil
128128
defined ++ imported ++ recur(using ctx.outer)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class Typer extends Namer
206206
else found
207207

208208
def selection(imp: ImportInfo, name: Name, checkBounds: Boolean): Type =
209-
imp.sym.info match
209+
imp.importSym.info match
210210
case ImportType(expr) =>
211211
val pre = expr.tpe
212212
var denot = pre.memberBasedOnFlags(name, required, excluded)
@@ -222,8 +222,8 @@ class Typer extends Namer
222222
if unimported.isEmpty || !unimported.contains(pre.termSymbol) then
223223
return pre.select(name, denot)
224224
case _ =>
225-
if imp.sym.isCompleting then
226-
report.warning(i"cyclic ${imp.sym}, ignored", pos)
225+
if imp.importSym.isCompleting then
226+
report.warning(i"cyclic ${imp.importSym}, ignored", pos)
227227
NoType
228228

229229
/** The type representing a named import with enclosing name when imported
@@ -393,7 +393,7 @@ class Typer extends Namer
393393
val namedImp = namedImportRef(curImport)
394394
if (namedImp.exists)
395395
recurAndCheckNewOrShadowed(namedImp, NamedImport, ctx)(using outer)
396-
else if (isPossibleImport(WildImport) && !curImport.sym.isCompleting) {
396+
else if (isPossibleImport(WildImport) && !curImport.importSym.isCompleting) {
397397
val wildImp = wildImportRef(curImport)
398398
if (wildImp.exists)
399399
recurAndCheckNewOrShadowed(wildImp, WildImport, ctx)(using outer)

0 commit comments

Comments
 (0)