Skip to content

Commit b5864b4

Browse files
committed
Merge pull request #85 from dotty-staging/topic/language-features
Topic/language features
2 parents 2df29a2 + 3eab410 commit b5864b4

21 files changed

+384
-42
lines changed

src/dotty/language.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package dotty
2+
3+
object language {
4+
5+
class Feature
6+
7+
/** Allow higher-kinded type syntax (not yet checked) */
8+
val higherKinds = new Feature
9+
10+
/** Keep union types */
11+
val keepUnions = new Feature
12+
13+
/** No auto tupling */
14+
val noAutoTupling = new Feature
15+
16+
}

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,9 @@ object desugar {
180180
constr1.mods, constr1.name, tparams, vparamss, constr1.tpt, constr1.rhs)
181181

182182
// a reference to the class type, with all parameters given.
183-
val classTypeRef: Tree = {
184-
// Dotty deviation: Without type annotation infers Ident | AppliedTypeTree, which
183+
val classTypeRef/*: Tree*/ = {
184+
// -language:keepUnions difference: classTypeRef needs type annotation, otherwise
185+
// infers Ident | AppliedTypeTree, which
185186
// renders the :\ in companions below untypable.
186187
val tycon = Ident(cdef.name) withPos cdef.pos.startPos
187188
val tparams = impl.constr.tparams
@@ -728,7 +729,7 @@ object desugar {
728729
*/
729730
private object VarPattern {
730731
def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match {
731-
case id: Ident => Some((id, TypeTree())) // Dotty deviation: No auto-tupling
732+
case id: Ident => Some(id, TypeTree())
732733
case Typed(id: Ident, tpt) => Some((id, tpt))
733734
case _ => None
734735
}

src/dotty/tools/dotc/config/Settings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ object Settings {
6060
copy(aliases = aliases :+ abbrv)(idx)
6161

6262
def dependsOn[U](setting: Setting[U], value: U): Setting[T] =
63-
copy(depends = depends :+ ((setting, value)))(idx) // Dotty deviation: no auto-tupling
63+
copy(depends = depends :+ (setting, value))(idx)
6464

6565
def valueIn(state: SettingsState): T =
6666
state.value(idx).asInstanceOf[T]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ object Contexts {
273273
newctx.implicitsCache = null
274274
newctx.setCreationTrace()
275275
// Dotty deviation: Scala2x allows access to private members implicitCache and setCreationTrace
276-
// even from a subclass prefix. Dotty (and Java) do not. I think that's a bug in Scala2x.
276+
// even from a subclass prefix. Dotty (and Java) do not. It's confirmed as a bug in Scala2x.
277277
newctx.asInstanceOf[FreshContext]
278278
}
279279

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class Definitions {
215215
lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable")
216216
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")
217217
lazy val ProductClass = ctx.requiredClass("scala.Product")
218+
lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass
218219

219220
// Annotation base classes
220221
lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation")
@@ -285,13 +286,14 @@ class Definitions {
285286
object FunctionType {
286287
def apply(args: List[Type], resultType: Type) =
287288
FunctionClass(args.length).typeRef.appliedTo(args ::: resultType :: Nil)
288-
def unapply(ft: Type): Option[(List[Type], Type)] = { // Dotty deviation: Type annotation needed because inferred type
289-
// is Some[(List[Type], Type)] | None, which is not a legal unapply type.
289+
def unapply(ft: Type)/*: Option[(List[Type], Type)]*/ = {
290+
// -language:keepUnions difference: unapply needs result type because inferred type
291+
// is Some[(List[Type], Type)] | None, which is not a legal unapply type.
290292
val tsym = ft.typeSymbol
291293
lazy val targs = ft.argInfos
292294
if ((FunctionClasses contains tsym) &&
293295
(targs.length - 1 <= MaxFunctionArity) &&
294-
(FunctionClass(targs.length - 1) == tsym)) Some((targs.init, targs.last)) // Dotty deviation: no auto-tupling
296+
(FunctionClass(targs.length - 1) == tsym)) Some(targs.init, targs.last)
295297
else None
296298
}
297299
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ object StdNames {
375375
val hashCode_ : N = "hashCode"
376376
val hash_ : N = "hash"
377377
val head: N = "head"
378+
val higherKinds: N = "higherKinds"
378379
val identity: N = "identity"
379380
val implicitly: N = "implicitly"
380381
val in: N = "in"
@@ -387,6 +388,7 @@ object StdNames {
387388
val isInstanceOf_ : N = "isInstanceOf"
388389
val isInstanceOf_Ob : N = "$isInstanceOf"
389390
val java: N = "java"
391+
val keepUnions: N = "keepUnions"
390392
val key: N = "key"
391393
val lang: N = "lang"
392394
val length: N = "length"
@@ -415,6 +417,7 @@ object StdNames {
415417
val next: N = "next"
416418
val nmeNewTermName: N = "newTermName"
417419
val nmeNewTypeName: N = "newTypeName"
420+
val noAutoTupling: N = "noAutoTupling"
418421
val normalize: N = "normalize"
419422
val notifyAll_ : N = "notifyAll"
420423
val notify_ : N = "notify"

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import collection.mutable
99
import collection.immutable.BitSet
1010
import scala.reflect.io.AbstractFile
1111
import Decorators.SymbolIteratorDecorator
12-
import ast.tpd
12+
import ast._
1313
import annotation.tailrec
1414
import util.SimpleMap
1515
import util.Stats
@@ -195,6 +195,18 @@ object SymDenotations {
195195
final def hasAnnotation(cls: Symbol)(implicit ctx: Context) =
196196
dropOtherAnnotations(annotations, cls).nonEmpty
197197

198+
/** Optionally, the arguments of the first annotation matching the given class symbol */
199+
final def getAnnotationArgs(cls: Symbol)(implicit ctx: Context): Option[List[tpd.Tree]] =
200+
dropOtherAnnotations(annotations, cls) match {
201+
case annot :: _ =>
202+
Some(
203+
annot.tree match {
204+
case Trees.Apply(_, args) => args
205+
case _ => Nil
206+
})
207+
case nil => None
208+
}
209+
198210
/** Add given annotation to the annotations of this denotation */
199211
final def addAnnotation(annot: Annotation): Unit =
200212
annotations = annot :: myAnnotations

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,20 @@ class TypeApplications(val self: Type) extends AnyVal {
170170
/** The type arguments of this type's base type instance wrt.`base`.
171171
* Existential types in arguments are disallowed.
172172
*/
173-
final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve noBounds
173+
final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] =
174+
baseArgInfos(base) mapConserve noBounds
174175

175176
/** The type arguments of this type's base type instance wrt.`base`.
176177
* Existential types in arguments are approximanted by their lower bound.
177178
*/
178-
final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve boundsToLo
179+
final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] =
180+
baseArgInfos(base) mapConserve boundsToLo
179181

180182
/** The type arguments of this type's base type instance wrt.`base`.
181183
* Existential types in arguments are approximanted by their upper bound.
182184
*/
183-
final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve boundsToHi
185+
final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] =
186+
baseArgInfos(base) mapConserve boundsToHi
184187

185188
/** The first type argument of the base type instance wrt `base` of this type */
186189
final def firstBaseArgInfo(base: Symbol)(implicit ctx: Context): Type = base.typeParams match {
@@ -193,8 +196,11 @@ class TypeApplications(val self: Type) extends AnyVal {
193196
/** The base type including all type arguments of this type.
194197
* Existential types in arguments are returned as TypeBounds instances.
195198
*/
196-
final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type =
197-
self.baseTypeRef(base).appliedTo(baseArgInfos(base))
199+
final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = self.dealias match {
200+
case AndType(tp1, tp2) => tp1.baseTypeWithArgs(base) & tp2.baseTypeWithArgs(base)
201+
case OrType(tp1, tp2) => tp1.baseTypeWithArgs(base) | tp2.baseTypeWithArgs(base)
202+
case _ => self.baseTypeRef(base).appliedTo(baseArgInfos(base))
203+
}
198204

199205
/** Translate a type of the form From[T] to To[T], keep other types as they are.
200206
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
@@ -205,7 +211,7 @@ class TypeApplications(val self: Type) extends AnyVal {
205211
else self
206212

207213
/** If this is an encoding of a (partially) applied type, return its arguments,
208-
* otherwise return Nil.
214+
* otherwise return Nil.
209215
* Existential types in arguments are returned as TypeBounds instances.
210216
*/
211217
final def argInfos(implicit ctx: Context): List[Type] = {

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,19 @@ class TypeComparer(initctx: Context) extends DotClass {
890890

891891
/** Try to distribute `&` inside type, detect and handle conflicts */
892892
private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match {
893+
// opportunistically merge same-named refinements
894+
// this does not change anything semantically (i.e. merging or not merging
895+
// gives =:= types), but it keeps the type smaller.
896+
case tp1: RefinedType =>
897+
tp2 match {
898+
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
899+
tp1.derivedRefinedType(
900+
tp1.parent & tp2.parent,
901+
tp1.refinedName,
902+
tp1.refinedInfo & tp2.refinedInfo)
903+
case _ =>
904+
NoType
905+
}
893906
case tp1: TypeBounds =>
894907
tp2 match {
895908
case tp2: TypeBounds => tp1 & tp2
@@ -935,18 +948,6 @@ class TypeComparer(initctx: Context) extends DotClass {
935948
case _ =>
936949
rt1 & tp2
937950
}
938-
case tp1: RefinedType =>
939-
// opportunistically merge same-named refinements
940-
// this does not change anything semantically (i.e. merging or not merging
941-
// gives =:= types), but it keeps the type smaller.
942-
tp2 match {
943-
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
944-
tp1.derivedRefinedType(
945-
tp1.parent & tp2.parent, tp1.refinedName,
946-
tp1.refinedInfo & tp2.refinedInfo)
947-
case _ =>
948-
NoType
949-
}
950951
case tp1: TypeVar if tp1.isInstantiated =>
951952
tp1.underlying & tp2
952953
case tp1: AnnotatedType =>
@@ -957,6 +958,16 @@ class TypeComparer(initctx: Context) extends DotClass {
957958

958959
/** Try to distribute `|` inside type, detect and handle conflicts */
959960
private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match {
961+
case tp1: RefinedType =>
962+
tp2 match {
963+
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
964+
tp1.derivedRefinedType(
965+
tp1.parent | tp2.parent,
966+
tp1.refinedName,
967+
tp1.refinedInfo | tp2.refinedInfo)
968+
case _ =>
969+
NoType
970+
}
960971
case tp1: TypeBounds =>
961972
tp2 match {
962973
case tp2: TypeBounds => tp1 | tp2

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
55
import SymDenotations._
66
import config.Printers._
77
import Decorators._
8+
import StdNames._
89
import util.SimpleMap
910

1011
trait TypeOps { this: Context =>
@@ -76,6 +77,37 @@ trait TypeOps { this: Context =>
7677
def apply(tp: Type) = simplify(tp, this)
7778
}
7879

80+
/** Approximate union type by intersection of its dominators.
81+
* See Type#approximateUnion for an explanation.
82+
*/
83+
def approximateUnion(tp: Type): Type = {
84+
/** a faster version of cs1 intersect cs2 */
85+
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
86+
val cs2AsSet = new util.HashSet[ClassSymbol](100)
87+
cs2.foreach(cs2AsSet.addEntry)
88+
cs1.filter(cs2AsSet.contains)
89+
}
90+
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
91+
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {
92+
case c :: rest =>
93+
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
94+
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
95+
}
96+
if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp
97+
else tp match {
98+
case tp: OrType =>
99+
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
100+
val doms = dominators(commonBaseClasses, Nil)
101+
doms.map(tp.baseTypeWithArgs).reduceLeft(AndType.apply)
102+
case tp @ AndType(tp1, tp2) =>
103+
tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
104+
case tp: RefinedType =>
105+
tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
106+
case _ =>
107+
tp
108+
}
109+
}
110+
79111
/** A type is volatile if its DNF contains an alternative of the form
80112
* {P1, ..., Pn}, {N1, ..., Nk}, where the Pi are parent typerefs and the
81113
* Nj are refinement names, and one the 4 following conditions is met:
@@ -266,6 +298,44 @@ trait TypeOps { this: Context =>
266298
}
267299
parentRefs
268300
}
301+
302+
/** Is `feature` enabled in class `owner`?
303+
* This is the case if one of the following two alternatives holds:
304+
*
305+
* 1. The feature is imported by a named import
306+
*
307+
* import owner.feature
308+
*
309+
* (the feature may be bunched with others, or renamed, but wildcard imports
310+
* don't count).
311+
*
312+
* 2. The feature is enabled by a compiler option
313+
*
314+
* - language:<prefix>feature
315+
*
316+
* where <prefix> is the full name of the owner followed by a "." minus
317+
* the prefix "dotty.language.".
318+
*/
319+
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
320+
def toPrefix(sym: Symbol): String =
321+
if (sym eq defn.LanguageModuleClass) "" else toPrefix(sym.owner) + sym.name + "."
322+
def featureName = toPrefix(owner) + feature
323+
def hasImport(implicit ctx: Context): Boolean = (
324+
ctx.importInfo != null
325+
&& ( (ctx.importInfo.site.widen.typeSymbol eq owner)
326+
&& ctx.importInfo.originals.contains(feature)
327+
||
328+
{ var c = ctx.outer
329+
while (c.importInfo eq ctx.importInfo) c = c.outer
330+
hasImport(c)
331+
}))
332+
def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_")
333+
hasImport || hasOption
334+
}
335+
336+
/** Is auto-tupling enabled? */
337+
def canAutoTuple =
338+
!featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling)
269339
}
270340

271341
object TypeOps {

0 commit comments

Comments
 (0)