Skip to content

Commit 9a5f9c8

Browse files
committed
Add scala.Dynamic language feature check.
1 parent 12a246a commit 9a5f9c8

File tree

8 files changed

+54
-3
lines changed

8 files changed

+54
-3
lines changed

src/dotty/language.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ object language {
1313
/** No auto tupling */
1414
val noAutoTupling = new Feature
1515

16+
/** Allow use of scala.Dynamic */
17+
val dynamics = new Feature
18+
1619
}

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,8 +424,10 @@ class Definitions {
424424
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
425425
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
426426
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
427-
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
427+
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
428428
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
429+
lazy val Scala2LanguageModuleRef = ctx.requiredModule("scala.language")
430+
def Scala2LanguageModuleClass(implicit ctx: Context) = Scala2LanguageModuleRef.symbol.moduleClass.asClass
429431
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
430432

431433
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")

src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ object StdNames {
383383
val delayedInit: N = "delayedInit"
384384
val delayedInitArg: N = "delayedInit$body"
385385
val drop: N = "drop"
386+
val dynamics: N = "dynamics"
386387
val dummyApply: N = "<dummy-apply>"
387388
val elem: N = "elem"
388389
val emptyValDef: N = "emptyValDef"

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
455455
*/
456456
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
457457
def toPrefix(sym: Symbol): String =
458-
if (sym eq defn.LanguageModuleClass) "" else toPrefix(sym.owner) + sym.name + "."
458+
if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleRef)) ""
459+
else toPrefix(sym.owner) + sym.name + "."
459460
def featureName = toPrefix(owner) + feature
460461
def hasImport(implicit ctx: Context): Boolean = (
461462
ctx.importInfo != null
@@ -477,6 +478,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
477478
def scala2Mode =
478479
featureEnabled(defn.LanguageModuleClass, nme.Scala2)
479480

481+
def dynamicsEnabled = {
482+
featureEnabled(defn.Scala2LanguageModuleClass, nme.dynamics) ||
483+
featureEnabled(defn.LanguageModuleClass, nme.dynamics)
484+
}
485+
480486
def testScala2Mode(msg: String, pos: Position) = {
481487
if (scala2Mode) migrationWarning(msg, pos)
482488
scala2Mode

src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import config.Printers
1212
import java.lang.System.currentTimeMillis
1313
import core.Mode
1414
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
15+
import dotty.tools.dotc.core.Symbols.Symbol
1516

1617
object Reporter {
1718
class Error(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, ERROR)
@@ -68,6 +69,29 @@ trait Reporting { this: Context =>
6869
def featureWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
6970
reporter.report(new FeatureWarning(msg, pos))
7071

72+
private[this] var reportedFeatures = Set[Symbol]()
73+
def featureWarning(pos: SourcePosition, feature: String, featureDescription: String, featureTrait: Symbol, required: Boolean): Unit = {
74+
val req = if (required) "needs to" else "should"
75+
val prefix = if (scala2Mode) "scala." else "dotty."
76+
val fqname = prefix + "language." + feature
77+
78+
val explain = {
79+
if (reportedFeatures.contains(featureTrait)) ""
80+
else {
81+
s"""|
82+
|This can be achieved by adding the import clause 'import $fqname'
83+
|or by setting the compiler option -language:$feature.
84+
|See the Scala docs for value $fqname for a discussion
85+
|why the feature $req be explicitly enabled.""".stripMargin
86+
}
87+
}
88+
reportedFeatures += featureTrait
89+
90+
val msg = s"$featureDescription $req be enabled\nby making the implicit value $fqname visible.$explain"
91+
if (required) error(msg, pos)
92+
else featureWarning(msg, pos)
93+
}
94+
7195
def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
7296
reporter.report(new Warning(msg, pos))
7397

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
10981098
.withType(dummy.nonMemberTermRef)
10991099
checkVariance(impl1)
11001100
if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.pos)
1101-
assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls)
1101+
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls)
1102+
if (ctx.phase.isTyper && cdef1.tpe <:< defn.DynamicType && !ctx.dynamicsEnabled) {
1103+
val isRequired = parents1.exists(_.tpe =:= defn.DynamicType)
1104+
ctx.featureWarning(cdef.pos, nme.dynamics.toString, "extension of type scala.Dynamic", cls, isRequired)
1105+
}
1106+
cdef1
11021107

11031108
// todo later: check that
11041109
// 1. If class is non-abstract, it is instantiatable:

tests/neg/dynamicNoImport.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo extends scala.Dynamic // error
2+
trait Bar extends scala.Dynamic // error
3+
object Baz extends scala.Dynamic // error
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
package foo {
3+
import scala.language.dynamics
4+
class Bar extends scala.Dynamic
5+
}
6+
7+
class Baz extends foo.Bar

0 commit comments

Comments
 (0)