Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes and improvements to erasure #11695

Merged
merged 19 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ object Feature:
private def deprecated(str: String): TermName =
QualifiedName(nme.deprecated, str.toTermName)

private val Xdependent = experimental("dependent")
private val XnamedTypeArguments = experimental("namedTypeArguments")
private val XgenericNumberLiterals = experimental("genericNumberLiterals")
private val Xmacros = experimental("macros")
private val symbolLiterals: TermName = deprecated("symbolLiterals")
private val namedTypeArguments = experimental("namedTypeArguments")
private val genericNumberLiterals = experimental("genericNumberLiterals")
private val scala2macros = experimental("macros")

val dependent = experimental("dependent")
val erasedDefinitions = experimental("erasedDefinitions")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Erased is not only about definitions. It is also about the term values they provide. This is completely lost with this new name. The erased class concept is also about erasing term definitions and uses as arguments. Therefore, the scope of the feature has not really changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But erased is a modifier on a definition, not a term. Anyway, people usually do not know what a term is. We'd have to say erasedExpressions, but I believe Definitions is preferable, since that's where the erased goes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that if you erase a definition you also erase its RHS, and if you erase a parameter, you also erase its argument follows naturally since it's the only thing that makes sense. But the trigger is the erased on a definition. Or parameter, but I think it's OK to include that in the meaning.

val symbolLiterals: TermName = deprecated("symbolLiterals")

/** Is `feature` enabled by by a command-line setting? The enabling setting is
*
Expand Down Expand Up @@ -62,15 +64,13 @@ object Feature:

def dynamicsEnabled(using Context): Boolean = enabled(nme.dynamics)

def dependentEnabled(using Context) = enabled(Xdependent)

def namedTypeArgsEnabled(using Context) = enabled(XnamedTypeArguments)
def dependentEnabled(using Context) = enabled(dependent)

def genericNumberLiteralsEnabled(using Context) = enabled(XgenericNumberLiterals)
def namedTypeArgsEnabled(using Context) = enabled(namedTypeArguments)

def symbolLiteralsEnabled(using Context) = enabled(symbolLiterals)
def genericNumberLiteralsEnabled(using Context) = enabled(genericNumberLiterals)

def scala2ExperimentalMacroEnabled(using Context) = enabled(Xmacros)
def scala2ExperimentalMacroEnabled(using Context) = enabled(scala2macros)

def sourceVersionSetting(using Context): SourceVersion =
SourceVersion.valueOf(ctx.settings.source.value)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings {
// Extremely experimental language features
val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting("-Yno-kind-polymorphism", "Disable kind polymorphism.")
val YexplicitNulls: Setting[Boolean] = BooleanSetting("-Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.")
val YerasedTerms: Setting[Boolean] = BooleanSetting("-Yerased-terms", "Allows the use of erased terms.")
val YcheckInit: Setting[Boolean] = BooleanSetting("-Ysafe-init", "Ensure safe initialization of objects")
val YrequireTargetName: Setting[Boolean] = BooleanSetting("-Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation")
val YerasedTerms: Setting[Boolean] = BooleanSetting("-Yerased-terms", "(disabled, use -language:experimental.erasedDefinitions instead)")

/** Area-specific debug output */
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ object Flags {
/** An export forwarder */
val (Exported @ _, _, _) = newFlags(41, "exported")

/** Labeled with `erased` modifier (erased value) */
val (_, Erased @ _, _) = newFlags(42, "erased")
/** Labeled with `erased` modifier (erased value or class) */
val (Erased @ _, _, _) = newFlags(42, "erased")

/** An opaque type alias or a class containing one */
val (Opaque @ _, _, _) = newFlags(43, "opaque")
Expand Down Expand Up @@ -439,13 +439,13 @@ object Flags {

/** Flags representing source modifiers */
private val CommonSourceModifierFlags: FlagSet =
commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent)
commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased)

val TypeSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open

val TermSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy

/** Flags representing modifiers that can appear in trees */
val ModifierFlags: FlagSet =
Expand Down Expand Up @@ -515,12 +515,12 @@ object Flags {
val RetainedModuleValAndClassFlags: FlagSet =
AccessFlags | Package | Case |
Synthetic | JavaDefined | JavaStatic | Artifact |
Lifted | MixedIn | Specialized | ConstructorProxy | Invisible
Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | Erased

/** Flags that can apply to a module val */
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
Override | Final | Method | Implicit | Given | Lazy |
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased | Transparent
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Transparent

/** Flags that can apply to a module class */
val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | Enum
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ object StdNames {
val equalsNumNum : N = "equalsNumNum"
val equalsNumObject : N = "equalsNumObject"
val equals_ : N = "equals"
val erased: N = "erased"
val error: N = "error"
val eval: N = "eval"
val eqlAny: N = "eqlAny"
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -729,10 +729,10 @@ class TreePickler(pickler: TastyPickler) {
if flags.is(Transparent) then writeModTag(TRANSPARENT)
if flags.is(Infix) then writeModTag(INFIX)
if flags.is(Invisible) then writeModTag(INVISIBLE)
if (flags.is(Erased)) writeModTag(ERASED)
if (isTerm) {
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (flags.is(Given)) writeModTag(GIVEN)
if (flags.is(Erased)) writeModTag(ERASED)
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
if (flags.is(Mutable)) writeModTag(MUTABLE)
Expand Down
36 changes: 19 additions & 17 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import util.Chars
import scala.annotation.{tailrec, switch}
import rewrites.Rewrites.{patch, overlapsPatch}
import reporting._
import config.Feature.{sourceVersion, migrateTo3, dependentEnabled, symbolLiteralsEnabled}
import config.Feature
import config.Feature.{sourceVersion, migrateTo3}
import config.SourceVersion._
import config.SourceVersion

Expand Down Expand Up @@ -179,6 +180,7 @@ object Parsers {

def isIdent = in.isIdent
def isIdent(name: Name) = in.isIdent(name)
def isErased = isIdent(nme.erased) && in.erasedEnabled
def isSimpleLiteral =
simpleLiteralTokens.contains(in.token)
|| isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token)
Expand Down Expand Up @@ -446,19 +448,18 @@ object Parsers {
* Parameters appear in reverse order.
*/
var placeholderParams: List[ValDef] = Nil
var languageImportContext: Context = ctx

def checkNoEscapingPlaceholders[T](op: => T): T =
val savedPlaceholderParams = placeholderParams
val savedLanguageImportContext = languageImportContext
val savedLanguageImportContext = in.languageImportContext
placeholderParams = Nil
try op
finally
placeholderParams match
case vd :: _ => syntaxError(UnboundPlaceholderParameter(), vd.span)
case _ =>
placeholderParams = savedPlaceholderParams
languageImportContext = savedLanguageImportContext
in.languageImportContext = savedLanguageImportContext

def isWildcard(t: Tree): Boolean = t match {
case Ident(name1) => placeholderParams.nonEmpty && name1 == placeholderParams.head.name
Expand Down Expand Up @@ -1141,7 +1142,7 @@ object Parsers {
Quote(t)
}
else
if !symbolLiteralsEnabled(using languageImportContext) then
if !in.featureEnabled(Feature.symbolLiterals) then
report.errorOrMigrationWarning(
em"""symbol literal '${in.name} is no longer supported,
|use a string literal "${in.name}" or an application Symbol("${in.name}") instead,
Expand Down Expand Up @@ -1374,7 +1375,7 @@ object Parsers {
functionRest(Nil)
}
else {
imods = modifiers(funTypeArgMods)
if isErased then imods = addModifier(imods)
val paramStart = in.offset
val ts = funArgType() match {
case Ident(name) if name != tpnme.WILDCARD && in.token == COLON =>
Expand Down Expand Up @@ -1572,7 +1573,7 @@ object Parsers {
typeIdent()
else
def singletonArgs(t: Tree): Tree =
if in.token == LPAREN && dependentEnabled(using languageImportContext)
if in.token == LPAREN && in.featureEnabled(Feature.dependent)
then singletonArgs(AppliedTypeTree(t, inParens(commaSeparated(singleton))))
else t
singletonArgs(simpleType1())
Expand Down Expand Up @@ -1861,7 +1862,7 @@ object Parsers {

def expr(location: Location): Tree = {
val start = in.offset
def isSpecialClosureStart = in.lookahead.token == ERASED
def isSpecialClosureStart = in.lookahead.isIdent(nme.erased) && in.erasedEnabled
if in.token == IMPLICIT then
closure(start, location, modifiers(BitSet(IMPLICIT)))
else if in.token == LPAREN && isSpecialClosureStart then
Expand Down Expand Up @@ -2093,7 +2094,7 @@ object Parsers {
Nil
else
var mods1 = mods
if in.token == ERASED then mods1 = addModifier(mods1)
if isErased then mods1 = addModifier(mods1)
try
commaSeparated(() => binding(mods1))
finally
Expand Down Expand Up @@ -2684,14 +2685,14 @@ object Parsers {
case FINAL => Mod.Final()
case IMPLICIT => Mod.Implicit()
case GIVEN => Mod.Given()
case ERASED => Mod.Erased()
case LAZY => Mod.Lazy()
case OVERRIDE => Mod.Override()
case PRIVATE => Mod.Private()
case PROTECTED => Mod.Protected()
case SEALED => Mod.Sealed()
case IDENTIFIER =>
name match {
case nme.erased if in.erasedEnabled => Mod.Erased()
case nme.inline => Mod.Inline()
case nme.opaque => Mod.Opaque()
case nme.open => Mod.Open()
Expand Down Expand Up @@ -2775,8 +2776,6 @@ object Parsers {
normalize(loop(start))
}

val funTypeArgMods: BitSet = BitSet(ERASED)

/** Wrap annotation or constructor in New(...).<init> */
def wrapNew(tpt: Tree): Select = Select(New(tpt), nme.CONSTRUCTOR)

Expand Down Expand Up @@ -2899,10 +2898,13 @@ object Parsers {
def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })

def paramMods() =
if in.token == IMPLICIT then addParamMod(() => Mod.Implicit())
if in.token == IMPLICIT then
addParamMod(() => Mod.Implicit())
else
if isIdent(nme.using) then addParamMod(() => Mod.Given())
if in.token == ERASED then addParamMod(() => Mod.Erased())
if isIdent(nme.using) then
addParamMod(() => Mod.Given())
if isErased then
addParamMod(() => Mod.Erased())

def param(): ValDef = {
val start = in.offset
Expand Down Expand Up @@ -2959,7 +2961,7 @@ object Parsers {
if in.token == RPAREN && !prefix && !impliedMods.is(Given) then Nil
else
val clause =
if prefix && !isIdent(nme.using) then param() :: Nil
if prefix && !isIdent(nme.using) && !isIdent(nme.erased) then param() :: Nil
else
paramMods()
if givenOnly && !impliedMods.is(Given) then
Expand Down Expand Up @@ -3032,7 +3034,7 @@ object Parsers {
def mkImport(outermost: Boolean = false): ImportConstr = (tree, selectors) =>
val imp = Import(tree, selectors)
if isLanguageImport(tree) then
languageImportContext = languageImportContext.importContext(imp, NoSymbol)
in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol)
for
case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors
if allSourceVersionNames.contains(imported)
Expand Down
20 changes: 16 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import scala.annotation.{switch, tailrec}
import scala.collection.mutable
import scala.collection.immutable.{SortedMap, BitSet}
import rewrites.Rewrites.patch
import config.Feature
import config.Feature.migrateTo3
import config.SourceVersion._
import reporting.Message
Expand Down Expand Up @@ -185,6 +186,13 @@ object Scanners {
error(s"illegal combination of -rewrite targets: ${enabled(0).name} and ${enabled(1).name}")
}

private var myLanguageImportContext: Context = ctx
def languageImportContext = myLanguageImportContext
final def languageImportContext_=(c: Context) = myLanguageImportContext = c

def featureEnabled(name: TermName) = Feature.enabled(name)(using languageImportContext)
def erasedEnabled = featureEnabled(Feature.erasedDefinitions) || ctx.settings.YerasedTerms.value

/** All doc comments kept by their end position in a `Map`.
*
* Note: the map is necessary since the comments are looked up after an
Expand Down Expand Up @@ -215,8 +223,7 @@ object Scanners {
private val commentBuf = CharBuffer()

private def handleMigration(keyword: Token): Token =
if keyword == ERASED && !ctx.settings.YerasedTerms.value then IDENTIFIER
else if scala3keywords.contains(keyword) && migrateTo3 then treatAsIdent()
if scala3keywords.contains(keyword) && migrateTo3 then treatAsIdent()
else keyword

private def treatAsIdent(): Token =
Expand Down Expand Up @@ -907,7 +914,9 @@ object Scanners {
reset()
next

class LookaheadScanner() extends Scanner(source, offset)
class LookaheadScanner() extends Scanner(source, offset) {
override def languageImportContext = Scanner.this.languageImportContext
}

/** Skip matching pairs of `(...)` or `[...]` parentheses.
* @pre The current token is `(` or `[`
Expand Down Expand Up @@ -1009,14 +1018,17 @@ object Scanners {
}

def isSoftModifier: Boolean =
token == IDENTIFIER && softModifierNames.contains(name)
token == IDENTIFIER
&& (softModifierNames.contains(name) || name == nme.erased && erasedEnabled)

def isSoftModifierInModifierPosition: Boolean =
isSoftModifier && inModifierPosition()

def isSoftModifierInParamModifierPosition: Boolean =
isSoftModifier && lookahead.token != COLON

def isErased: Boolean = isIdent(nme.erased) && erasedEnabled

def canStartStatTokens =
if migrateTo3 then canStartStatTokens2 else canStartStatTokens3

Expand Down
15 changes: 6 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ abstract class TokensCommon {
//final val THEN = 60; enter(THEN, "then")
//final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
//final val ENUM = 62; enter(ENUM, "enum")
//final val ERASED = 63; enter(ERASED, "erased")

/** special symbols */
final val COMMA = 70; enter(COMMA, "','")
Expand Down Expand Up @@ -181,10 +180,9 @@ object Tokens extends TokensCommon {
final val THEN = 60; enter(THEN, "then")
final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
final val ENUM = 62; enter(ENUM, "enum")
final val ERASED = 63; enter(ERASED, "erased")
final val GIVEN = 64; enter(GIVEN, "given")
final val EXPORT = 65; enter(EXPORT, "export")
final val MACRO = 67; enter(MACRO, "macro") // TODO: remove
final val GIVEN = 63; enter(GIVEN, "given")
final val EXPORT = 64; enter(EXPORT, "export")
final val MACRO = 65; enter(MACRO, "macro") // TODO: remove

/** special symbols */
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
Expand Down Expand Up @@ -240,8 +238,7 @@ object Tokens extends TokensCommon {

final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens

final val localModifierTokens: TokenSet = BitSet(
ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY, ERASED)
final val localModifierTokens: TokenSet = BitSet(ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY)

final val accessModifierTokens: TokenSet = BitSet(
PRIVATE, PROTECTED)
Expand All @@ -251,7 +248,7 @@ object Tokens extends TokensCommon {

final val modifierTokensOrCase: TokenSet = modifierTokens | BitSet(CASE)

final val modifierFollowers = modifierTokens | defIntroTokens
final val modifierFollowers = modifierTokensOrCase | defIntroTokens

/** Is token only legal as start of statement (eof also included)? */
final val mustStartStatTokens: TokenSet = defIntroTokens | modifierTokens | BitSet(IMPORT, EXPORT, PACKAGE)
Expand Down Expand Up @@ -283,7 +280,7 @@ object Tokens extends TokensCommon {
*/
final val startParamTokens: BitSet = modifierTokens | BitSet(VAL, VAR, AT)

final val scala3keywords = BitSet(ENUM, ERASED, GIVEN)
final val scala3keywords = BitSet(ENUM, GIVEN)

final val endMarkerTokens = identifierTokens | BitSet(IF, WHILE, FOR, MATCH, TRY, NEW, THROW, GIVEN, VAL, THIS)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
else PrintableFlags(isType)
if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= GivenOrImplicit // drop implicit/given from classes
val rawFlags = if (sym.exists) sym.flags else mods.flags
if (rawFlags.is(Param)) flagMask = flagMask &~ Given
if (rawFlags.is(Param)) flagMask = flagMask &~ Given &~ Erased
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might also want to remove Inline

Copy link
Contributor Author

@odersky odersky Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, inline is per param, not per clause

val flags = rawFlags & flagMask
var flagsText = toTextFlags(sym, flags)
val annotations =
Expand Down
Loading