Skip to content

Topic/language features #85

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 7 commits into from
Mar 20, 2014
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
16 changes: 16 additions & 0 deletions src/dotty/language.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dotty

object language {

class Feature

/** Allow higher-kinded type syntax (not yet checked) */
val higherKinds = new Feature

/** Keep union types */
val keepUnions = new Feature

/** No auto tupling */
val noAutoTupling = new Feature

}
7 changes: 4 additions & 3 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ object desugar {
constr1.mods, constr1.name, tparams, vparamss, constr1.tpt, constr1.rhs)

// a reference to the class type, with all parameters given.
val classTypeRef: Tree = {
// Dotty deviation: Without type annotation infers Ident | AppliedTypeTree, which
val classTypeRef/*: Tree*/ = {
// -language:keepUnions difference: classTypeRef needs type annotation, otherwise
// infers Ident | AppliedTypeTree, which
// renders the :\ in companions below untypable.
val tycon = Ident(cdef.name) withPos cdef.pos.startPos
val tparams = impl.constr.tparams
Expand Down Expand Up @@ -728,7 +729,7 @@ object desugar {
*/
private object VarPattern {
def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match {
case id: Ident => Some((id, TypeTree())) // Dotty deviation: No auto-tupling
case id: Ident => Some(id, TypeTree())
case Typed(id: Ident, tpt) => Some((id, tpt))
case _ => None
}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ object Settings {
copy(aliases = aliases :+ abbrv)(idx)

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

def valueIn(state: SettingsState): T =
state.value(idx).asInstanceOf[T]
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ object Contexts {
newctx.implicitsCache = null
newctx.setCreationTrace()
// Dotty deviation: Scala2x allows access to private members implicitCache and setCreationTrace
// even from a subclass prefix. Dotty (and Java) do not. I think that's a bug in Scala2x.
// even from a subclass prefix. Dotty (and Java) do not. It's confirmed as a bug in Scala2x.
newctx.asInstanceOf[FreshContext]
}

Expand Down
8 changes: 5 additions & 3 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class Definitions {
lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable")
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")
lazy val ProductClass = ctx.requiredClass("scala.Product")
lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass

// Annotation base classes
lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation")
Expand Down Expand Up @@ -285,13 +286,14 @@ class Definitions {
object FunctionType {
def apply(args: List[Type], resultType: Type) =
FunctionClass(args.length).typeRef.appliedTo(args ::: resultType :: Nil)
def unapply(ft: Type): Option[(List[Type], Type)] = { // Dotty deviation: Type annotation needed because inferred type
// is Some[(List[Type], Type)] | None, which is not a legal unapply type.
def unapply(ft: Type)/*: Option[(List[Type], Type)]*/ = {
// -language:keepUnions difference: unapply needs result type because inferred type
// is Some[(List[Type], Type)] | None, which is not a legal unapply type.
val tsym = ft.typeSymbol
lazy val targs = ft.argInfos
if ((FunctionClasses contains tsym) &&
(targs.length - 1 <= MaxFunctionArity) &&
(FunctionClass(targs.length - 1) == tsym)) Some((targs.init, targs.last)) // Dotty deviation: no auto-tupling
(FunctionClass(targs.length - 1) == tsym)) Some(targs.init, targs.last)
else None
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ object StdNames {
val hashCode_ : N = "hashCode"
val hash_ : N = "hash"
val head: N = "head"
val higherKinds: N = "higherKinds"
val identity: N = "identity"
val implicitly: N = "implicitly"
val in: N = "in"
Expand All @@ -387,6 +388,7 @@ object StdNames {
val isInstanceOf_ : N = "isInstanceOf"
val isInstanceOf_Ob : N = "$isInstanceOf"
val java: N = "java"
val keepUnions: N = "keepUnions"
val key: N = "key"
val lang: N = "lang"
val length: N = "length"
Expand Down Expand Up @@ -415,6 +417,7 @@ object StdNames {
val next: N = "next"
val nmeNewTermName: N = "newTermName"
val nmeNewTypeName: N = "newTypeName"
val noAutoTupling: N = "noAutoTupling"
val normalize: N = "normalize"
val notifyAll_ : N = "notifyAll"
val notify_ : N = "notify"
Expand Down
14 changes: 13 additions & 1 deletion src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import collection.mutable
import collection.immutable.BitSet
import scala.reflect.io.AbstractFile
import Decorators.SymbolIteratorDecorator
import ast.tpd
import ast._
import annotation.tailrec
import util.SimpleMap
import util.Stats
Expand Down Expand Up @@ -195,6 +195,18 @@ object SymDenotations {
final def hasAnnotation(cls: Symbol)(implicit ctx: Context) =
dropOtherAnnotations(annotations, cls).nonEmpty

/** Optionally, the arguments of the first annotation matching the given class symbol */
final def getAnnotationArgs(cls: Symbol)(implicit ctx: Context): Option[List[tpd.Tree]] =
dropOtherAnnotations(annotations, cls) match {
case annot :: _ =>
Some(
annot.tree match {
case Trees.Apply(_, args) => args
case _ => Nil
})
case nil => None
}

/** Add given annotation to the annotations of this denotation */
final def addAnnotation(annot: Annotation): Unit =
annotations = annot :: myAnnotations
Expand Down
18 changes: 12 additions & 6 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,20 @@ class TypeApplications(val self: Type) extends AnyVal {
/** The type arguments of this type's base type instance wrt.`base`.
* Existential types in arguments are disallowed.
*/
final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = baseArgInfos(base) mapConserve noBounds
final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] =
baseArgInfos(base) mapConserve noBounds

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

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

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

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

/** If this is an encoding of a (partially) applied type, return its arguments,
* otherwise return Nil.
* otherwise return Nil.
* Existential types in arguments are returned as TypeBounds instances.
*/
final def argInfos(implicit ctx: Context): List[Type] = {
Expand Down
35 changes: 23 additions & 12 deletions src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,19 @@ class TypeComparer(initctx: Context) extends DotClass {

/** Try to distribute `&` inside type, detect and handle conflicts */
private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match {
// opportunistically merge same-named refinements
// this does not change anything semantically (i.e. merging or not merging
// gives =:= types), but it keeps the type smaller.
case tp1: RefinedType =>
tp2 match {
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
tp1.derivedRefinedType(
tp1.parent & tp2.parent,
tp1.refinedName,
tp1.refinedInfo & tp2.refinedInfo)
case _ =>
NoType
}
case tp1: TypeBounds =>
tp2 match {
case tp2: TypeBounds => tp1 & tp2
Expand Down Expand Up @@ -935,18 +948,6 @@ class TypeComparer(initctx: Context) extends DotClass {
case _ =>
rt1 & tp2
}
case tp1: RefinedType =>
// opportunistically merge same-named refinements
// this does not change anything semantically (i.e. merging or not merging
// gives =:= types), but it keeps the type smaller.
tp2 match {
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
tp1.derivedRefinedType(
tp1.parent & tp2.parent, tp1.refinedName,
tp1.refinedInfo & tp2.refinedInfo)
case _ =>
NoType
}
case tp1: TypeVar if tp1.isInstantiated =>
tp1.underlying & tp2
case tp1: AnnotatedType =>
Expand All @@ -957,6 +958,16 @@ class TypeComparer(initctx: Context) extends DotClass {

/** Try to distribute `|` inside type, detect and handle conflicts */
private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match {
case tp1: RefinedType =>
tp2 match {
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
tp1.derivedRefinedType(
tp1.parent | tp2.parent,
tp1.refinedName,
tp1.refinedInfo | tp2.refinedInfo)
case _ =>
NoType
}
case tp1: TypeBounds =>
tp2 match {
case tp2: TypeBounds => tp1 | tp2
Expand Down
70 changes: 70 additions & 0 deletions src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._
import SymDenotations._
import config.Printers._
import Decorators._
import StdNames._
import util.SimpleMap

trait TypeOps { this: Context =>
Expand Down Expand Up @@ -76,6 +77,37 @@ trait TypeOps { this: Context =>
def apply(tp: Type) = simplify(tp, this)
}

/** Approximate union type by intersection of its dominators.
* See Type#approximateUnion for an explanation.
*/
def approximateUnion(tp: Type): Type = {
/** a faster version of cs1 intersect cs2 */
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
val cs2AsSet = new util.HashSet[ClassSymbol](100)
cs2.foreach(cs2AsSet.addEntry)
cs1.filter(cs2AsSet.contains)
}
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {
case c :: rest =>
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
}
if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp
else tp match {
case tp: OrType =>
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
val doms = dominators(commonBaseClasses, Nil)
doms.map(tp.baseTypeWithArgs).reduceLeft(AndType.apply)
case tp @ AndType(tp1, tp2) =>
tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
case tp: RefinedType =>
tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
case _ =>
tp
}
}

/** A type is volatile if its DNF contains an alternative of the form
* {P1, ..., Pn}, {N1, ..., Nk}, where the Pi are parent typerefs and the
* Nj are refinement names, and one the 4 following conditions is met:
Expand Down Expand Up @@ -266,6 +298,44 @@ trait TypeOps { this: Context =>
}
parentRefs
}

/** Is `feature` enabled in class `owner`?
* This is the case if one of the following two alternatives holds:
*
* 1. The feature is imported by a named import
*
* import owner.feature
*
* (the feature may be bunched with others, or renamed, but wildcard imports
* don't count).
*
* 2. The feature is enabled by a compiler option
*
* - language:<prefix>feature
*
* where <prefix> is the full name of the owner followed by a "." minus
* the prefix "dotty.language.".
*/
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
def toPrefix(sym: Symbol): String =
if (sym eq defn.LanguageModuleClass) "" else toPrefix(sym.owner) + sym.name + "."
def featureName = toPrefix(owner) + feature
def hasImport(implicit ctx: Context): Boolean = (
ctx.importInfo != null
&& ( (ctx.importInfo.site.widen.typeSymbol eq owner)
&& ctx.importInfo.originals.contains(feature)
||
{ var c = ctx.outer
while (c.importInfo eq ctx.importInfo) c = c.outer
hasImport(c)
}))
def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_")
hasImport || hasOption
}

/** Is auto-tupling enabled? */
def canAutoTuple =
!featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling)
}

object TypeOps {
Expand Down
Loading