Skip to content

Introduce Transparent flag #10458

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 8 commits into from
Nov 24, 2020
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
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Inline()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Inline)

case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.EmptyFlags)
case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Transparent)

case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)
}

/** Modifiers and annotations for definitions
Expand Down
24 changes: 12 additions & 12 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -286,35 +286,35 @@ trait ConstraintHandling {
}
}

/** If `tp` is an intersection such that some operands are mixin trait instances
* and others are not, replace as many mixin trait instances as possible with Any
/** If `tp` is an intersection such that some operands are transparent trait instances
* and others are not, replace as many transparent trait instances as possible with Any
* as long as the result is still a subtype of `bound`. But fall back to the
* original type if the resulting widened type is a supertype of all dropped
* types (since in this case the type was not a true intersection of mixin traits
* types (since in this case the type was not a true intersection of transparent traits
* and other types to start with).
*/
def dropMixinTraits(tp: Type, bound: Type)(using Context): Type =
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
var kept: Set[Type] = Set() // types to keep since otherwise bound would not fit
var dropped: List[Type] = List() // the types dropped so far, last one on top

def dropOneMixinTrait(tp: Type): Type =
def dropOneTransparentTrait(tp: Type): Type =
val tpd = tp.dealias
if tpd.typeSymbol.isMixinTrait && !tpd.isLambdaSub && !kept.contains(tpd) then
if tpd.typeSymbol.isTransparentTrait && !tpd.isLambdaSub && !kept.contains(tpd) then
dropped = tpd :: dropped
defn.AnyType
else tpd match
case AndType(tp1, tp2) =>
val tp1w = dropOneMixinTrait(tp1)
val tp1w = dropOneTransparentTrait(tp1)
if tp1w ne tp1 then tp1w & tp2
else
val tp2w = dropOneMixinTrait(tp2)
val tp2w = dropOneTransparentTrait(tp2)
if tp2w ne tp2 then tp1 & tp2w
else tpd
case _ =>
tp

def recur(tp: Type): Type =
val tpw = dropOneMixinTrait(tp)
val tpw = dropOneTransparentTrait(tp)
if tpw eq tp then tp
else if tpw <:< bound then recur(tpw)
else
Expand All @@ -324,15 +324,15 @@ trait ConstraintHandling {

val tpw = recur(tp)
if (tpw eq tp) || dropped.forall(_ frozen_<:< tpw) then tp else tpw
end dropMixinTraits
end dropTransparentTraits

/** Widen inferred type `inst` with upper `bound`, according to the following rules:
* 1. If `inst` is a singleton type, or a union containing some singleton types,
* widen (all) the singleton type(s), provided the result is a subtype of `bound`
* (i.e. `inst.widenSingletons <:< bound` succeeds with satisfiable constraint)
* 2. If `inst` is a union type, approximate the union type from above by an intersection
* of all common base types, provided the result is a subtype of `bound`.
* 3. drop mixin traits from intersections (see @dropMixinTraits)
* 3. drop transparent traits from intersections (see @dropTransparentTraits)
*
* Don't do these widenings if `bound` is a subtype of `scala.Singleton`.
* Also, if the result of these widenings is a TypeRef to a module class,
Expand All @@ -357,7 +357,7 @@ trait ConstraintHandling {

val wideInst =
if isSingleton(bound) then inst
else dropMixinTraits(widenOr(widenSingle(inst)), bound)
else dropTransparentTraits(widenOr(widenSingle(inst)), bound)
wideInst match
case wideInst: TypeRef if wideInst.symbol.is(Module) =>
TermRef(wideInst.prefix, wideInst.symbol.sourceModule)
Expand Down
7 changes: 3 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ class Definitions {
@tu lazy val InvariantBetweenAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InvariantBetween")
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")
@tu lazy val MigrationAnnot: ClassSymbol = requiredClass("scala.annotation.migration")
@tu lazy val MixinAnnot: ClassSymbol = requiredClass("scala.annotation.mixin")
@tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait")
@tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native")
@tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated")
@tu lazy val SourceFileAnnot: ClassSymbol = requiredClass("scala.annotation.internal.SourceFile")
Expand Down Expand Up @@ -940,7 +940,6 @@ class Definitions {
@tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter")
@tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix")
@tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface")
@tu lazy val InfixAnnot: ClassSymbol = requiredClass("scala.annotation.infix")
@tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName")
@tu lazy val VarargsAnnot: ClassSymbol = requiredClass("scala.annotation.varargs")

Expand Down Expand Up @@ -1507,8 +1506,8 @@ class Definitions {
def isInfix(sym: Symbol)(using Context): Boolean =
(sym eq Object_eq) || (sym eq Object_ne)

@tu lazy val assumedMixinTraits =
Set(ComparableClass, ProductClass, SerializableClass,
@tu lazy val assumedTransparentTraits =
Set[Symbol](ComparableClass, ProductClass, SerializableClass,
// add these for now, until we had a chance to retrofit 2.13 stdlib
// we should do a more through sweep through it then.
requiredClass("scala.collection.SortedOps"),
Expand Down
16 changes: 11 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ object Flags {
/** Symbol is a Java default method */
val (_, DefaultMethod @ _, _) = newFlags(38, "<defaultmethod>")

/** Symbol is a transparent inline method or trait */
val (Transparent @ _, _, _) = newFlags(39, "transparent")

/** Symbol is an enum class or enum case (if used with case) */
val (Enum @ _, EnumVal @ _, _) = newFlags(40, "enum")

Expand All @@ -354,14 +357,16 @@ object Flags {
/** An opaque type alias or a class containing one */
val (Opaque @ _, _, _) = newFlags(43, "opaque")

/** An infix method or type */
val (Infix @ _, _, _) = newFlags(44, "infix")

// ------------ Flags following this one are not pickled ----------------------------------

/** Symbol is not a member of its owner */
val (NonMember @ _, _, _) = newFlags(45, "<non-member>")
val (NonMember @ _, _, _) = newFlags(49, "<non-member>")

/** Denotation is in train of being loaded and completed, used to catch cyclic dependencies */
val (Touched @ _, _, _) = newFlags(48, "<touched>")
val (Touched @ _, _, _) = newFlags(50, "<touched>")

/** Class has been lifted out to package level, local value has been lifted out to class level */
val (Lifted @ _, _, _) = newFlags(51, "<lifted>")
Expand Down Expand Up @@ -419,7 +424,7 @@ object Flags {

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

val TypeSourceModifierFlags: FlagSet =
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open
Expand Down Expand Up @@ -449,7 +454,7 @@ object Flags {
* is completed)
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined)
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined, Transparent)

/** A value that's unstable unless complemented with a Stable flag */
val UnstableValueFlags: FlagSet = Mutable | Method
Expand Down Expand Up @@ -499,7 +504,7 @@ object Flags {
/** Flags that can apply to a module val */
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
Override | Final | Method | Implicit | Given | Lazy |
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased | Transparent

/** Flags that can apply to a module class */
val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | Enum
Expand Down Expand Up @@ -576,4 +581,5 @@ object Flags {
val SyntheticParam: FlagSet = Synthetic | Param
val SyntheticTermParam: FlagSet = Synthetic | TermParam
val SyntheticTypeParam: FlagSet = Synthetic | TypeParam
val TransparentTrait: FlagSet = Trait | Transparent
}
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1109,9 +1109,10 @@ object SymDenotations {
final def isEffectivelySealed(using Context): Boolean =
isOneOf(FinalOrSealed) || isClass && !isOneOf(EffectivelyOpenFlags)

final def isMixinTrait(using Context): Boolean =
isClass
&& (hasAnnotation(defn.MixinAnnot) || defn.assumedMixinTraits.contains(symbol.asClass))
final def isTransparentTrait(using Context): Boolean =
isAllOf(TransparentTrait)
|| defn.assumedTransparentTraits.contains(symbol)
|| isClass && hasAnnotation(defn.TransparentTraitAnnot)

/** The class containing this denotation which has the given effective name. */
final def enclosingClassNamed(name: Name)(using Context): Symbol = {
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2674,8 +2674,8 @@ object TypeComparer {
def widenInferred(inst: Type, bound: Type)(using Context): Type =
comparing(_.widenInferred(inst, bound))

def dropMixinTraits(tp: Type, bound: Type)(using Context): Type =
comparing(_.dropMixinTraits(tp, bound))
def dropTransparentTraits(tp: Type, bound: Type)(using Context): Type =
comparing(_.dropTransparentTraits(tp, bound))

def constrainPatternType(pat: Type, scrut: Type)(using Context): Boolean =
comparing(_.constrainPatternType(pat, scrut))
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@ class TreePickler(pickler: TastyPickler) {
if (flags.is(Local)) writeModTag(LOCAL)
if (flags.is(Synthetic)) writeModTag(SYNTHETIC)
if (flags.is(Artifact)) writeModTag(ARTIFACT)
if flags.is(Transparent) then writeModTag(TRANSPARENT)
if flags.is(Infix) then writeModTag(INFIX)
if (isTerm) {
if (flags.is(Implicit)) writeModTag(IMPLICIT)
if (flags.is(Given)) writeModTag(GIVEN)
Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -667,15 +667,14 @@ class TreeUnpickler(reader: TastyReader,
case PARAMalias => addFlag(SuperParamAlias)
case EXPORTED => addFlag(Exported)
case OPEN => addFlag(Open)
case TRANSPARENT => addFlag(Transparent)
case INFIX => addFlag(Infix)
case PRIVATEqualified =>
readByte()
privateWithin = readWithin
case PROTECTEDqualified =>
addFlag(Protected)
privateWithin = readWithin
case SUPERTRAIT =>
readByte()
annotFns = (_ => Annotation(defn.MixinAnnot)) :: annotFns
case ANNOTATION =>
annotFns = readAnnot :: annotFns
case tag =>
Expand Down
7 changes: 2 additions & 5 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2732,6 +2732,7 @@ object Parsers {
case nme.opaque => Mod.Opaque()
case nme.open => Mod.Open()
case nme.transparent => Mod.Transparent()
case nme.infix => Mod.Infix()
}
}

Expand Down Expand Up @@ -2807,11 +2808,7 @@ object Parsers {
}
else
mods
val result = normalize(loop(start))
for case mod @ Mod.Transparent() <- result.mods do
if !result.is(Inline) then
syntaxError(em"`transparent` can only be used in conjunction with `inline`", mod.span)
result
normalize(loop(start))
}

val funTypeArgMods: BitSet = BitSet(ERASED)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,5 @@ object Tokens extends TokensCommon {

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

final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent)
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent, nme.infix)
}
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -897,8 +897,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (rawFlags.is(Param)) flagMask = flagMask &~ Given
val flags = rawFlags & flagMask
var flagsText = toTextFlags(sym, flags)
if mods.hasMod(classOf[untpd.Mod.Transparent]) then
flagsText = "transparent " ~ flagsText
val annotations =
if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree)
else mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ object SymUtils:

/** Is symbol assumed or declared as an infix symbol? */
def isDeclaredInfix(using Context): Boolean =
self.hasAnnotation(defn.InfixAnnot)
self.is(Infix)
|| defn.isInfix(self)
|| self.name.isUnapplyName
&& self.owner.is(Module)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ trait Applications extends Compatibility {
&& tree.tpe.classSymbol.isEnumCase
&& tree.tpe.widen.isValueType
then
val widened = TypeComparer.dropMixinTraits(
val widened = TypeComparer.dropTransparentTraits(
tree.tpe.parents.reduceLeft(TypeComparer.andType(_, _)),
pt)
if widened <:< pt then Typed(tree, TypeTree(widened))
Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,11 @@ object Checking {
if (sym.isType)
fail(TypesAndTraitsCantBeImplicit())
}
if sym.is(Transparent) then
if sym.isType then
if !sym.is(Trait) then fail(em"`transparent` can only be used for traits")
else
if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods")
if (!sym.isClass && sym.is(Abstract))
fail(OnlyClassesCanBeAbstract(sym))
// note: this is not covered by the next test since terms can be abstract (which is a dual-mode flag)
Expand Down Expand Up @@ -781,7 +786,7 @@ trait Checking {
}

/** Check that `tree` is a valid infix operation. That is, if the
* operator is alphanumeric, it must be declared `@infix`.
* operator is alphanumeric, it must be declared `infix`.
*/
def checkValidInfix(tree: untpd.InfixOp, meth: Symbol)(using Context): Unit = {
tree.op match {
Expand All @@ -802,7 +807,7 @@ trait Checking {
else
("method", (n: Name) => s"method syntax .$n(...)")
report.deprecationWarning(
i"""Alphanumeric $kind $name is not declared @infix; it should not be used as infix operator.
i"""Alphanumeric $kind $name is not declared `infix`; it should not be used as infix operator.
|The operation can be rewritten automatically to `$name` under -deprecation -rewrite.
|Or rewrite to ${alternative(name)} manually.""",
tree.op.srcPos)
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,7 @@ object PrepareInlineable {

/** The type ascription `rhs: tpt`, unless `original` is `transparent`. */
def wrapRHS(original: untpd.DefDef, tpt: Tree, rhs: Tree)(using Context): Tree =
if original.mods.hasMod(classOf[untpd.Mod.Transparent]) then rhs
else Typed(rhs, tpt)
if original.mods.is(Transparent) then rhs else Typed(rhs, tpt)

/** Return result of evaluating `op`, but drop `Inline` flag and `Body` annotation
* of `sym` in case that leads to errors.
Expand Down
Loading