diff --git a/community-build/community-projects/stdLib213 b/community-build/community-projects/stdLib213 index e7b881ee61a4..a336cf270669 160000 --- a/community-build/community-projects/stdLib213 +++ b/community-build/community-projects/stdLib213 @@ -1 +1 @@ -Subproject commit e7b881ee61a49bb5435ae54c2d23340b352c4802 +Subproject commit a336cf27066904aa4cd517fb980395028a9c305f diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 11d494ce039d..6b0e61e55494 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -200,6 +200,8 @@ 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 Super()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.SuperTrait) } /** Modifiers and annotations for definitions diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index d0a52808df6a..fc8149a57a31 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -300,8 +300,12 @@ trait ConstraintHandling[AbstractContext] { * (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. (currently not enabled, see #9028) If `inst` is an intersection with some restricted base types, drop - * the restricted base types from the intersection, provided the result is a subtype of `bound`. + * 3. If `inst` is an intersection such that some operands are super trait instances + * and others are not, replace as many super 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 super traits + * and other types to start with). * * 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, @@ -313,21 +317,38 @@ trait ConstraintHandling[AbstractContext] { */ def widenInferred(inst: Type, bound: Type)(implicit actx: AbstractContext): Type = - def isRestricted(tp: Type) = tp.typeSymbol == defn.EnumValueClass // for now, to be generalized later + def dropSuperTraits(tp: Type): 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 dropOneSuperTrait(tp: Type): Type = + val tpd = tp.dealias + if tpd.typeSymbol.isSuperTrait && !tpd.isLambdaSub && !kept.contains(tpd) then + dropped = tpd :: dropped + defn.AnyType + else tpd match + case AndType(tp1, tp2) => + val tp1w = dropOneSuperTrait(tp1) + if tp1w ne tp1 then tp1w & tp2 + else + val tp2w = dropOneSuperTrait(tp2) + if tp2w ne tp2 then tp1 & tp2w + else tpd + case _ => + tp - def dropRestricted(tp: Type): Type = tp.dealias match - case tpd @ AndType(tp1, tp2) => - if isRestricted(tp1) then tp2 - else if isRestricted(tp2) then tp1 + def recur(tp: Type): Type = + val tpw = dropOneSuperTrait(tp) + if tpw eq tp then tp + else if tpw <:< bound then recur(tpw) else - val tpw = tpd.derivedAndType(dropRestricted(tp1), dropRestricted(tp2)) - if tpw ne tpd then tpw else tp - case _ => - tp + kept += dropped.head + dropped = dropped.tail + recur(tp) - def widenRestricted(tp: Type) = - val tpw = dropRestricted(tp) - if (tpw ne tp) && (tpw <:< bound) then tpw else tp + val tpw = recur(tp) + if (tpw eq tp) || dropped.forall(_ frozen_<:< tpw) then tp else tpw + end dropSuperTraits def widenOr(tp: Type) = val tpw = tp.widenUnion @@ -343,10 +364,7 @@ trait ConstraintHandling[AbstractContext] { val wideInst = if isSingleton(bound) then inst - else /*widenRestricted*/(widenOr(widenSingle(inst))) - // widenRestricted is currently not called since it's special cased in `dropEnumValue` - // in `Namer`. It's left in here in case we want to generalize the scheme to other - // "protected inheritance" classes. + else dropSuperTraits(widenOr(widenSingle(inst))) wideInst match case wideInst: TypeRef if wideInst.symbol.is(Module) => TermRef(wideInst.prefix, wideInst.symbol.sourceModule) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6dba2ed925c4..7f94860998ba 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -639,7 +639,6 @@ class Definitions { @tu lazy val EnumClass: ClassSymbol = ctx.requiredClass("scala.Enum") @tu lazy val Enum_ordinal: Symbol = EnumClass.requiredMethod(nme.ordinal) - @tu lazy val EnumValueClass: ClassSymbol = ctx.requiredClass("scala.runtime.EnumValue") @tu lazy val EnumValuesClass: ClassSymbol = ctx.requiredClass("scala.runtime.EnumValues") @tu lazy val ProductClass: ClassSymbol = ctx.requiredClass("scala.Product") @tu lazy val Product_canEqual : Symbol = ProductClass.requiredMethod(nme.canEqual_) @@ -804,6 +803,7 @@ class Definitions { @tu lazy val ScalaStrictFPAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.strictfp") @tu lazy val ScalaStaticAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.static") @tu lazy val SerialVersionUIDAnnot: ClassSymbol = ctx.requiredClass("scala.SerialVersionUID") + @tu lazy val SuperTraitAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.superTrait") @tu lazy val TASTYSignatureAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.internal.TASTYSignature") @tu lazy val TASTYLongSignatureAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.internal.TASTYLongSignature") @tu lazy val TailrecAnnot: ClassSymbol = ctx.requiredClass("scala.annotation.tailrec") @@ -1308,6 +1308,21 @@ class Definitions { def isInfix(sym: Symbol)(implicit ctx: Context): Boolean = (sym eq Object_eq) || (sym eq Object_ne) + @tu lazy val assumedSuperTraits = + Set(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. + ctx.requiredClass("scala.collection.SortedOps"), + ctx.requiredClass("scala.collection.StrictOptimizedSortedSetOps"), + ctx.requiredClass("scala.collection.generic.DefaultSerializable"), + ctx.requiredClass("scala.collection.generic.IsIterable"), + ctx.requiredClass("scala.collection.generic.IsIterableOnce"), + ctx.requiredClass("scala.collection.generic.IsMap"), + ctx.requiredClass("scala.collection.generic.IsSeq"), + ctx.requiredClass("scala.collection.generic.Subtractable"), + ctx.requiredClass("scala.collection.immutable.StrictOptimizedSeqOps") + ) + // ----- primitive value class machinery ------------------------------------------ /** This class would also be obviated by the implicit function type design */ diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0b8eb22034b0..c5e04b146007 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -222,8 +222,8 @@ object Flags { /** Labeled with `final` modifier */ val (Final @ _, _, _) = newFlags(6, "final") - /** A method symbol */ - val (_, Method @ _, HigherKinded @ _) = newFlags(7, "", "") // TODO drop HigherKinded + /** A method symbol / a super trait */ + val (_, Method @ _, SuperTrait @ _) = newFlags(7, "", "super") /** A (term or type) parameter to a class or method */ val (Param @ _, TermParam @ _, TypeParam @ _) = newFlags(8, "") @@ -439,7 +439,7 @@ object Flags { */ val FromStartFlags: FlagSet = commonFlags( Module, Package, Deferred, Method, Case, Enum, - HigherKinded, Param, ParamAccessor, + SuperTrait, Param, ParamAccessor, Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, Extension, NonMember, Implicit, Given, Permanent, Synthetic, diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index f51aec15c45d..7d6054545032 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1166,6 +1166,12 @@ object SymDenotations { final def isEffectivelySealed(using Context): Boolean = isOneOf(FinalOrSealed) || isClass && !isOneOf(EffectivelyOpenFlags) + final def isSuperTrait(using Context): Boolean = + isClass + && (is(SuperTrait) + || defn.assumedSuperTraits.contains(symbol.asClass) + || hasAnnotation(defn.SuperTraitAnnot)) + /** The class containing this denotation which has the given effective name. */ final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = { val cls = enclosingClass diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7f275cec0f03..470ce6b13822 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -705,6 +705,7 @@ class TreePickler(pickler: TastyPickler) { if (flags.is(Sealed)) writeModTag(SEALED) if (flags.is(Abstract)) writeModTag(ABSTRACT) if (flags.is(Trait)) writeModTag(TRAIT) + if flags.is(SuperTrait) then writeModTag(SUPERTRAIT) if (flags.is(Covariant)) writeModTag(COVARIANT) if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT) if (flags.is(Opaque)) writeModTag(OPAQUE) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 75b29cd4d78f..c4efc746de31 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -639,6 +639,7 @@ class TreeUnpickler(reader: TastyReader, case STATIC => addFlag(JavaStatic) case OBJECT => addFlag(Module) case TRAIT => addFlag(Trait) + case SUPERTRAIT => addFlag(SuperTrait) case ENUM => addFlag(Enum) case LOCAL => addFlag(Local) case SYNTHETIC => addFlag(Synthetic) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6c5eaf315ffa..f4dd74c77d15 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3434,7 +3434,7 @@ object Parsers { } } - /** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef + /** TmplDef ::= ([‘case’] ‘class’ | [‘super’] ‘trait’) ClassDef * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef @@ -3444,6 +3444,8 @@ object Parsers { in.token match { case TRAIT => classDef(start, posMods(start, addFlag(mods, Trait))) + case SUPERTRAIT => + classDef(start, posMods(start, addFlag(mods, Trait | SuperTrait))) case CLASS => classDef(start, posMods(start, mods)) case CASECLASS => diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 70cf1213c67d..9c4f16137e36 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -586,7 +586,8 @@ object Scanners { currentRegion = r.outer case _ => - /** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + => COLONEOL + /** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SUPER + TRAIT => SUPERTRAIT + * SEMI + ELSE => ELSE, COLON + => COLONEOL * - Insert missing OUTDENTs at EOF */ def postProcessToken(): Unit = { @@ -602,6 +603,10 @@ object Scanners { if (token == CLASS) fuse(CASECLASS) else if (token == OBJECT) fuse(CASEOBJECT) else reset() + case SUPER => + lookAhead() + if token == TRAIT then fuse(SUPERTRAIT) + else reset() case SEMI => lookAhead() if (token != ELSE) reset() diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 33792ea1d7ae..e961f80c0c94 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -184,7 +184,8 @@ object Tokens extends TokensCommon { final val ERASED = 63; enter(ERASED, "erased") final val GIVEN = 64; enter(GIVEN, "given") final val EXPORT = 65; enter(EXPORT, "export") - final val MACRO = 66; enter(MACRO, "macro") // TODO: remove + final val SUPERTRAIT = 66; enter(SUPERTRAIT, "super trait") + final val MACRO = 67; enter(MACRO, "macro") // TODO: remove /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -233,7 +234,7 @@ object Tokens extends TokensCommon { final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet( THIS, SUPER, USCORE, LPAREN, AT) - final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT) + final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT, SUPERTRAIT) final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, GIVEN) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 19e24764867e..af27755c81fc 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -729,7 +729,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } private def Modifiers(sym: Symbol): Modifiers = untpd.Modifiers( - sym.flags & (if (sym.isType) ModifierFlags | VarianceFlags else ModifierFlags), + sym.flags & (if (sym.isType) ModifierFlags | VarianceFlags | SuperTrait else ModifierFlags), if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY, sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree)) @@ -839,7 +839,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } protected def templateText(tree: TypeDef, impl: Template): Text = { - val decl = modText(tree.mods, tree.symbol, keywordStr(if (tree.mods.is(Trait)) "trait" else "class"), isType = true) + val kw = + if tree.mods.is(SuperTrait) then "super trait" + else if tree.mods.is(Trait) then "trait" + else "class" + val decl = modText(tree.mods, tree.symbol, keywordStr(kw), isType = true) ( decl ~~ typeText(nameIdText(tree)) ~ withEnclosingDef(tree) { toTextTemplate(impl) } // ~ (if (tree.hasType && printDebug) i"[decls = ${tree.symbol.info.decls}]" else "") // uncomment to enable ) @@ -945,6 +949,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else if (sym.isPackageObject) "package object" else if (flags.is(Module) && flags.is(Case)) "case object" else if (sym.isClass && flags.is(Case)) "case class" + else if sym.isClass && flags.is(SuperTrait) then "super trait" else super.keyString(sym) } diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 29026bd4d175..8a0d5ee98684 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -100,7 +100,7 @@ class ExtractSemanticDB extends Phase: private def excludeChildren(sym: Symbol)(using Context): Boolean = !sym.exists - || sym.isAllOf(HigherKinded | Param) + || sym.is(Param) && sym.info.bounds.hi.isInstanceOf[Types.HKTypeLambda] /** Uses of this symbol where the reference has given span should be excluded from semanticdb */ private def excludeUse(qualifier: Option[Symbol], sym: Symbol)(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 0c326cfc3735..63b9ec70b4a1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -404,7 +404,6 @@ class Namer { typer: Typer => case _: TypeBoundsTree | _: MatchTypeTree => flags |= Deferred // Typedefs with Match rhs classify as abstract case LambdaTypeTree(_, body) => - flags |= HigherKinded analyzeRHS(body) case _ => if rhs.isEmpty || flags.is(Opaque) then flags |= Deferred @@ -1459,19 +1458,6 @@ class Namer { typer: Typer => // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") def isInlineVal = sym.isOneOf(FinalOrInline, butNot = Method | Mutable) - def isEnumValue(tp: Type) = tp.typeSymbol == defn.EnumValueClass - - // Drop EnumValue parents from inferred types of enum constants - def dropEnumValue(tp: Type): Type = tp.dealias match - case tpd @ AndType(tp1, tp2) => - if isEnumValue(tp1) then tp2 - else if isEnumValue(tp2) then tp1 - else - val tpw = tpd.derivedAndType(dropEnumValue(tp1), dropEnumValue(tp2)) - if tpw ne tpd then tpw else tp - case _ => - tp - // Widen rhs type and eliminate `|' but keep ConstantTypes if // definition is inline (i.e. final in Scala2) and keep module singleton types // instead of widening to the underlying module class types. @@ -1480,9 +1466,7 @@ class Namer { typer: Typer => def widenRhs(tp: Type): Type = tp.widenTermRefExpr.simplified match case ctp: ConstantType if isInlineVal => ctp - case tp => - val tp1 = ctx.typeComparer.widenInferred(tp, rhsProto) - if sym.is(Enum) then dropEnumValue(tp1) else tp1 + case tp => ctx.typeComparer.widenInferred(tp, rhsProto) // Replace aliases to Unit by Unit itself. If we leave the alias in // it would be erased to BoxedUnit. diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 8d6b93efe691..8fef08a3474c 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -388,7 +388,7 @@ VarDef ::= PatDef DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr) | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, , Nil, vparamss, EmptyTree, expr | Block) -TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef +TmplDef ::= ([‘case’] ‘class’ | [‘super’] ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef | ‘given’ GivenDef diff --git a/docs/docs/reference/other-new-features/super-traits.md b/docs/docs/reference/other-new-features/super-traits.md new file mode 100644 index 000000000000..f7ca5d688ea1 --- /dev/null +++ b/docs/docs/reference/other-new-features/super-traits.md @@ -0,0 +1,86 @@ +--- +layout: doc-page +title: super traits +--- + +Traits are used in two roles: + + 1. As mixins for other classes and traits + 2. As types of vals, defs, or parameters + +Some traits are used primarily in the first role, and we usually do not want to see them in inferred types. An example is the `Product` trait that the compiler +adds as a super trait to every case class or case object. In Scala 2, this parent trait sometimes makes inferred types more complicated than they should be. Example: +```scala +trait Kind +case object Var extends Kind +case object Val extends Kind +val x = Set(if condition then Val else Var) +``` +Here, the inferred type of `x` is `Set[Kind & Product & Serializable]` whereas one would have hoped it to be `Set[Kind]`. The reasoning for this particular type to be inferred is as follows: + + - The type of the conditional above is the union type `Val | Var`. + - A union type is widened in type inference to the least supertype that is + not a union type. In the example, this type is `Kind & Product & Serializable` since all three traits are supertraits of both `Val` and `Var`. + So that type becomes the inferred element type of the set. + +Scala 3 allows one to mark a trait as a `super` trait, which means that it can be suppressed in type inference. Here's an example that follows the lines of the +code above, but now with a new super trait `S` instead of `Product`: +```scala +super trait S +trait Kind +object Var extends Kind, S +object Val extends Kind, S +val x = Set(if condition then Val else Var) +``` +Now `x` has inferred type `Set[Kind]`. The common super trait `S` does not +appear in the inferred type. + +### Super Traits + +The traits `scala.Product`, `java.lang.Serializable` and `java.lang.Comparable` +are treated automatically as super traits. Other traits can be turned into super traits, by adding the keyword `super` in front of `trait`, as shown above. + +Every trait can be declared as a super trait. Typically super traits are traits that influence the implementation of inheriting classes and traits and that are not usually used as types by themselves. Two examples from the +standard collection library: + + - `IterableOps`, which provides method implementations for an `Iterable` + - `StrictOptimizedSeqOps`, which optimises some of these implementations for + sequences with efficient indexing. + +Generally, any trait that is extended recursively is a good candidate to be +declared a super trait. + +### Retro-Fitting Scala 2 Libraries + +To allow cross-building between Scala 2 and 3, super traits can also be +introduced by adding the `@superTrait` annotation, which is defined in package `scala.annotation`. Example: +```scala +import scala.annotation.superTrait + +@superTrait trait StrictOptimizedSeqOps[+A, +CC[_], +C] ... +``` +The `@superTrait` annotation will be deprecated and removed in some later version of Scala when cross-building with Scala 2 will no longer be a concern. + +### Rules for Inference + +Super traits can be given as explicit types as usual. But they are often elided when types are inferred. Roughly, the rules for type inference say that super traits are dropped from intersections where possible. + +The precise rules are as follows: + + - When inferring a type of a type variable, or the type of a val, or the return type of a def, + - where that type is not higher-kinded, + - and where `B` is its known upper bound or `Any` if none exists: + - If the type inferred so far is of the form `T1 & ... & Tn` where + `n >= 1`, replace the maximal number of `Ti`s by `Any`, while ensuring that + the resulting type is still a subtype of the bound `B`. + - However, do not perform this widening if all types `Ti` can get replaced in that way. + +The last clause ensures that a single super trait instance such as `Product` is not widened to `Any`. Super trait instances are only dropped when they appear in conjunction with some other type. + +### Syntax + +Only the production `TmplDef` for class and trait definitions has to be changed. +The new version is: +``` +TmplDef ::= ([‘case’] ‘class’ | [‘super’] ‘trait’) ClassDef +``` diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 680a6d6982e9..b8f627760c8c 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -87,6 +87,8 @@ sidebar: subsection: - title: Trait Parameters url: docs/reference/other-new-features/trait-parameters.html + - title: Super Traits + url: docs/reference/other-new-features/super-traits.html - title: Creator Applications url: docs/reference/other-new-features/creator-applications.html - title: Export Clauses diff --git a/library/src-bootstrapped/scala/runtime/EnumValue.scala b/library/src-bootstrapped/scala/runtime/EnumValue.scala new file mode 100644 index 000000000000..8b1fa88a1076 --- /dev/null +++ b/library/src-bootstrapped/scala/runtime/EnumValue.scala @@ -0,0 +1,10 @@ +package scala.runtime + +super trait EnumValue extends Product, Serializable: + override def canEqual(that: Any) = this eq that.asInstanceOf[AnyRef] + override def productArity: Int = 0 + override def productPrefix: String = toString + override def productElement(n: Int): Any = + throw IndexOutOfBoundsException(n.toString) + override def productElementName(n: Int): String = + throw IndexOutOfBoundsException(n.toString) diff --git a/library/src-non-bootstrapped/Enum.scala b/library/src-non-bootstrapped/scala/Enum.scala similarity index 100% rename from library/src-non-bootstrapped/Enum.scala rename to library/src-non-bootstrapped/scala/Enum.scala diff --git a/library/src/scala/runtime/EnumValue.scala b/library/src-non-bootstrapped/scala/runtime/EnumValue.scala similarity index 100% rename from library/src/scala/runtime/EnumValue.scala rename to library/src-non-bootstrapped/scala/runtime/EnumValue.scala diff --git a/library/src/scala/annotation/superTrait.scala b/library/src/scala/annotation/superTrait.scala new file mode 100644 index 000000000000..d9dc7e27a4d5 --- /dev/null +++ b/library/src/scala/annotation/superTrait.scala @@ -0,0 +1,8 @@ +package scala.annotation + +/** Equivalent to declaring a super trait in Scala 3. This annotation + * should be used only for files that need to cross-compile between + * Scala 2 and 3. + */ +final class superTrait extends StaticAnnotation + diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 61976af89d96..4d8e4354f420 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -189,6 +189,7 @@ Standard-Section: "ASTs" TopLevelStat* STATIC -- Mapped to static Java member OBJECT -- An object or its class TRAIT -- A trait + SUPERTRAIT -- A super trait ENUM -- A enum class or enum case LOCAL -- private[this] or protected[this], used in conjunction with PRIVATE or PROTECTED SYNTHETIC -- Generated by Scala compiler @@ -359,6 +360,7 @@ object TastyFormat { final val OPEN = 40 final val PARAMEND = 41 final val PARAMalias = 42 + final val SUPERTRAIT = 43 // Cat. 2: tag Nat @@ -473,7 +475,7 @@ object TastyFormat { /** Useful for debugging */ def isLegalTag(tag: Int): Boolean = - firstSimpleTreeTag <= tag && tag <= PARAMalias || + firstSimpleTreeTag <= tag && tag <= SUPERTRAIT || firstNatTreeTag <= tag && tag <= RENAMED || firstASTTreeTag <= tag && tag <= BOUNDED || firstNatASTTreeTag <= tag && tag <= NAMEDARG || @@ -502,6 +504,7 @@ object TastyFormat { | STATIC | OBJECT | TRAIT + | SUPERTRAIT | ENUM | LOCAL | SYNTHETIC @@ -562,6 +565,7 @@ object TastyFormat { case STATIC => "STATIC" case OBJECT => "OBJECT" case TRAIT => "TRAIT" + case SUPERTRAIT => "SUPERTRAIT" case ENUM => "ENUM" case LOCAL => "LOCAL" case SYNTHETIC => "SYNTHETIC" diff --git a/tests/neg-custom-args/fatal-warnings/supertraits.scala b/tests/neg-custom-args/fatal-warnings/supertraits.scala new file mode 100644 index 000000000000..abadeb07d4f9 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/supertraits.scala @@ -0,0 +1,33 @@ +sealed super trait TA +sealed super trait TB +trait S +case object a extends S, TA, TB +case object b extends S, TA, TB + +object Test: + + def choose0[X](x: X, y: X): X = x + def choose1[X <: TA](x: X, y: X): X = x + def choose2[X <: TB](x: X, y: X): X = x + def choose3[X <: Product](x: X, y: X): X = x + def choose4[X <: TA & TB](x: X, y: X): X = x + + choose0(a, b) match + case _: TA => ??? + case _: TB => ??? // error: unreachable + + choose1(a, b) match + case _: TA => ??? + case _: TB => ??? // error: unreachable + + choose2(a, b) match + case _: TB => ??? + case _: TA => ??? // error: unreachable + + choose3(a, b) match + case _: Product => ??? + case _: TA => ??? // error: unreachable + + choose4(a, b) match + case _: (TA & TB) => ??? + case _: Product => ??? // error: unreachable \ No newline at end of file diff --git a/tests/neg/supertraits.scala b/tests/neg/supertraits.scala new file mode 100644 index 000000000000..1aed4f4f18a4 --- /dev/null +++ b/tests/neg/supertraits.scala @@ -0,0 +1,13 @@ +super trait S +trait A +class B extends A, S +class C extends A, S + +val x = if ??? then B() else C() +val x1: S = x // error + +case object a +case object b +val y = if ??? then a else b +val y1: Product = y // error +val y2: Serializable = y // error diff --git a/tests/pos/listAndVector.scala b/tests/pos/listAndVector.scala new file mode 100644 index 000000000000..c3ea8be08f54 --- /dev/null +++ b/tests/pos/listAndVector.scala @@ -0,0 +1,3 @@ +var xs = Set(List(0), Vector(0)) +var ys: Set[collection.immutable.AbstractSeq[Int]] = xs + diff --git a/tests/run/java-intersection/Test_2.scala b/tests/run/java-intersection/Test_2.scala index 90f22076e753..cbc39988340a 100644 --- a/tests/run/java-intersection/Test_2.scala +++ b/tests/run/java-intersection/Test_2.scala @@ -1,7 +1,7 @@ object Test { def main(args: Array[String]): Unit = { val a = new A_1 - val x = new java.io.Serializable {} + val x: java.io.Serializable = new java.io.Serializable {} a.foo(x) } }