Skip to content

Opaque types - selftype encoding #5300

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

Merged
merged 14 commits into from
Nov 12, 2018
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ class Compiler {
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
new ClassOf, // Expand `Predef.classOf` calls.
new RefChecks) :: // Various checks mostly related to abstract members and overriding
List(new TryCatchPatterns, // Compile cases in try/catch
List(new ElimOpaque, // Turn opaque into normal aliases
new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher, // Compile pattern matches
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
Expand Down
45 changes: 44 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,46 @@ object desugar {
}
}

/** Expand
*
* <mods> opaque type T = [Xs] => R
*
* to
*
* <mods> opaque type T = T.T
* synthetic object T {
* synthetic opaque type T >: [Xs] => R
* }
*
* The generated companion object will later (in Namer) be merged with the user-defined
* companion object, and the synthetic opaque type member will go into the self type.
*/
def opaqueAlias(tdef: TypeDef)(implicit ctx: Context): Tree =
if (tdef.rhs.isInstanceOf[TypeBoundsTree]) {
ctx.error(em"opaque type ${tdef.name} must be an alias type", tdef.pos)
tdef.withFlags(tdef.mods.flags &~ Opaque)
}
else {
def completeForwarder(fwd: Tree) = tdef.rhs match {
case LambdaTypeTree(tparams, tpt) =>
val tparams1 =
for (tparam <- tparams)
yield tparam.withMods(tparam.mods | Synthetic)
lambdaAbstract(tparams1,
AppliedTypeTree(fwd, tparams.map(tparam => Ident(tparam.name))))
case _ =>
fwd
}
val moduleName = tdef.name.toTermName
val aliasType = cpy.TypeDef(tdef)(
rhs = completeForwarder(Select(Ident(moduleName), tdef.name)))
val localType = tdef.withFlags(Synthetic | Opaque)
val companions = moduleDef(ModuleDef(
moduleName, Template(emptyConstructor, Nil, EmptyValDef, localType :: Nil))
.withFlags(Synthetic | Opaque))
Thicket(aliasType :: companions.toList)
}

/** The name of `mdef`, after checking that it does not redefine a Scala core class.
* If it does redefine, issue an error and return a mangled name instead of the original one.
*/
Expand Down Expand Up @@ -790,7 +830,10 @@ object desugar {

def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tree: ValDef => valDef(tree)
case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree
case tree: TypeDef =>
if (tree.isClassDef) classDef(tree)
else if (tree.mods.is(Opaque, butNot = Synthetic)) opaqueAlias(tree)
else tree
case tree: DefDef =>
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
else defDef(tree)
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ object Trees {
asInstanceOf[ThisTree[Untyped]]
}

protected def setMods(mods: untpd.Modifiers): Unit = myMods = mods
/** Destructively update modifiers. To be used with care. */
def setMods(mods: untpd.Modifiers): Unit = myMods = mods

/** The position of the name defined by this definition.
* This is a point position if the definition is synthetic, or a range position
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1102,9 +1102,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
private val InlinedCalls = new Property.Key[List[Tree]]

/** Record an enclosing inlined call.
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
* We assume parameters are never nested inside parameters.
*/
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
* We assume parameters are never nested inside parameters.
*/
override def inlineContext(call: Tree)(implicit ctx: Context): Context = {
// We assume enclosingInlineds is already normalized, and only process the new call with the head.
val oldIC = enclosingInlineds
Expand All @@ -1118,7 +1118,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}

/** All enclosing calls that are currently inlined, from innermost to outermost.
*/
*/
def enclosingInlineds(implicit ctx: Context): List[Tree] =
ctx.property(InlinedCalls).getOrElse(Nil)

Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Sealed() extends Mod(Flags.Sealed)

case class Opaque() extends Mod(Flags.Opaque)

case class Override() extends Mod(Flags.Override)

case class Abstract() extends Mod(Flags.Abstract)
Expand Down
12 changes: 9 additions & 3 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ class CheckRealizable(implicit ctx: Context) {
/** `Realizable` if `tp` has good bounds, a `HasProblem...` instance
* pointing to a bad bounds member otherwise. "Has good bounds" means:
*
* - all type members have good bounds
* - all type members have good bounds (except for opaque helpers)
* - all refinements of the underlying type have good bounds (except for opaque companions)
* - all base types are class types, and if their arguments are wildcards
* they have good bounds.
* - base types do not appear in multiple instances with different arguments.
Expand All @@ -114,10 +115,15 @@ class CheckRealizable(implicit ctx: Context) {
*/
private def boundsRealizability(tp: Type) = {

def isOpaqueCompanionThis = tp match {
case tp: ThisType => tp.cls.isOpaqueCompanion
case _ => false
}

val memberProblems =
for {
mbr <- tp.nonClassTypeMembers
if !(mbr.info.loBound <:< mbr.info.hiBound)
if !(mbr.info.loBound <:< mbr.info.hiBound) && !mbr.symbol.isOpaqueHelper
}
yield new HasProblemBounds(mbr.name, mbr.info)

Expand All @@ -126,7 +132,7 @@ class CheckRealizable(implicit ctx: Context) {
name <- refinedNames(tp)
if (name.isTypeName)
mbr <- tp.member(name).alternatives
if !(mbr.info.loBound <:< mbr.info.hiBound)
if !(mbr.info.loBound <:< mbr.info.hiBound) && !isOpaqueCompanionThis
}
yield new HasProblemBounds(name, mbr.info)

Expand Down
46 changes: 34 additions & 12 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,14 @@ object Flags {

final val AccessorOrSealed: FlagSet = Accessor.toCommonFlags

/** A mutable var */
/** A mutable var */
final val Mutable: FlagSet = termFlag(12, "mutable")

/** An opqaue type */
final val Opaque: FlagSet = typeFlag(12, "opaque")

final val MutableOrOpaque: FlagSet = Mutable.toCommonFlags

/** Symbol is local to current class (i.e. private[this] or protected[this]
* pre: Private or Protected are also set
*/
Expand All @@ -263,7 +268,7 @@ object Flags {
*/
final val ParamAccessor: FlagSet = termFlag(14, "<paramaccessor>")

/** A value or class implementing a module */
/** A value or class implementing a module */
final val Module: FlagSet = commonFlag(15, "module")
final val ModuleVal: FlagSet = Module.toTermFlags
final val ModuleClass: FlagSet = Module.toTypeFlags
Expand Down Expand Up @@ -435,15 +440,20 @@ object Flags {
// --------- Combined Flag Sets and Conjunctions ----------------------

/** Flags representing source modifiers */
final val SourceModifierFlags: FlagSet =
commonFlags(Private, Protected, Abstract, Final, Inline,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased)
private val CommonSourceModifierFlags: FlagSet =
commonFlags(Private, Protected, Final, Case, Implicit, Override, JavaStatic)

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

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

/** Flags representing modifiers that can appear in trees */
final val ModifierFlags: FlagSet =
SourceModifierFlags | Module | Param | Synthetic | Package | Local |
commonFlags(Mutable)
// | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags
TypeSourceModifierFlags.toCommonFlags |
TermSourceModifierFlags.toCommonFlags |
commonFlags(Module, Param, Synthetic, Package, Local, Mutable, Trait)

assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags)

Expand All @@ -454,7 +464,7 @@ object Flags {
final val FromStartFlags: FlagSet =
Module | Package | Deferred | Method.toCommonFlags |
HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags |
Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic |
Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic |
CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags |
NonMember | ImplicitCommon | Permanent | Synthetic |
SuperAccessorOrScala2x | Inline
Expand Down Expand Up @@ -516,7 +526,13 @@ object Flags {
Accessor | AbsOverride | Stable | Captured | Synchronized | Erased

/** Flags that can apply to a module class */
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass | Enum
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags |
ImplClass | Enum | Opaque

/** Flags that are copied from a synthetic companion to a user-defined one
* when the two are merged. See: Namer.mergeCompanionDefs
*/
final val RetainedSyntheticCompanionFlags: FlagSet = Opaque

/** Packages and package classes always have these flags set */
final val PackageCreationFlags: FlagSet =
Expand Down Expand Up @@ -629,6 +645,9 @@ object Flags {
/** A Java companion object */
final val JavaModule: FlagConjunction = allOf(JavaDefined, Module)

/** An opaque companion object */
final val OpaqueModule: FlagConjunction = allOf(Opaque, Module)

/** A Java companion object */
final val JavaProtected: FlagConjunction = allOf(JavaDefined, Protected)

Expand Down Expand Up @@ -665,15 +684,18 @@ object Flags {
/** Java symbol which is `protected` and `static` */
final val StaticProtected: FlagConjunction = allOf(JavaDefined, Protected, JavaStatic)

final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)

final val AbstractFinal: FlagConjunction = allOf(Abstract, Final)
final val AbstractSealed: FlagConjunction = allOf(Abstract, Sealed)
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)

final val SyntheticArtifact: FlagConjunction = allOf(Synthetic, Artifact)
final val SyntheticModule: FlagConjunction = allOf(Synthetic, Module)
final val SyntheticTermParam: FlagConjunction = allOf(Synthetic, TermParam)
final val SyntheticTypeParam: FlagConjunction = allOf(Synthetic, TypeParam)
final val SyntheticCase: FlagConjunction = allOf(Synthetic, Case)
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)
final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)
final val SyntheticOpaque: FlagConjunction = allOf(Synthetic, Opaque)

implicit def conjToFlagSet(conj: FlagConjunction): FlagSet =
FlagSet(conj.bits)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Periods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ abstract class Periods { self: Context =>
val period = this.period
period == p ||
period.runId == p.runId &&
this.phases(period.phaseId).sameParentsStartId ==
this.phases(p.phaseId).sameParentsStartId
this.phases(period.phaseId).sameBaseTypesStartId ==
this.phases(p.phaseId).sameBaseTypesStartId
}
}

Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ object Phases {
/** Can this transform change the parents of a class? */
def changesParents: Boolean = false

/** Can this transform change the base types of a type? */
def changesBaseTypes: Boolean = changesParents

def exists: Boolean = true

def initContext(ctx: FreshContext): Unit = ()
Expand All @@ -332,6 +335,7 @@ object Phases {

private[this] var mySameMembersStartId = NoPhaseId
private[this] var mySameParentsStartId = NoPhaseId
private[this] var mySameBaseTypesStartId = NoPhaseId

/** The sequence position of this phase in the given context where 0
* is reserved for NoPhase and the first real phase is at position 1.
Expand All @@ -351,6 +355,8 @@ object Phases {
// id of first phase where all symbols are guaranteed to have the same members as in this phase
final def sameParentsStartId: Int = mySameParentsStartId
// id of first phase where all symbols are guaranteed to have the same parents as in this phase
final def sameBaseTypesStartId: Int = mySameBaseTypesStartId
// id of first phase where all symbols are guaranteed to have the same base tpyes as in this phase

protected[Phases] def init(base: ContextBase, start: Int, end: Int): Unit = {
if (start >= FirstPhaseId)
Expand All @@ -363,6 +369,7 @@ object Phases {
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
mySameMembersStartId = if (changesMembers) id else prev.sameMembersStartId
mySameParentsStartId = if (changesParents) id else prev.sameParentsStartId
mySameBaseTypesStartId = if (changesBaseTypes) id else prev.sameBaseTypesStartId
}

protected[Phases] def init(base: ContextBase, id: Int): Unit = init(base, id, id)
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ object StdNames {
val WHILE_PREFIX: N = "while$"
val DEFAULT_EXCEPTION_NAME: N = "ex$"
val INITIALIZER_PREFIX: N = "initial$"
val COMPANION_MODULE_METHOD: N = "companion$module"
val COMPANION_CLASS_METHOD: N = "companion$class"
val BOUNDTYPE_ANNOT: N = "$boundType$"
val QUOTE: N = "'"
val TYPE_QUOTE: N = "type_'"
Expand Down Expand Up @@ -245,6 +243,7 @@ object StdNames {

// Compiler-internal
val ANYname: N = "<anyname>"
val COMPANION: N = "<companion>"
val CONSTRUCTOR: N = "<init>"
val STATIC_CONSTRUCTOR: N = "<clinit>"
val DEFAULT_CASE: N = "defaultCase$"
Expand Down
Loading