Skip to content

Add transparent types #4727

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

Closed
wants to merge 64 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
8df8f4e
Check baseclasses when determining purity of class
odersky Jun 18, 2018
8adf8db
Better purity predictions
odersky Jun 19, 2018
7d26a08
Exclude self and super constructor calls from purity warnings
odersky Jun 21, 2018
eb49110
Fix pure interface prediction for methods with default arguments
odersky Jun 23, 2018
603974e
Account for stable constrictors in ExtractAPI
odersky Jun 23, 2018
49a28de
Introduce UntypedSplice
odersky May 26, 2018
3bc3e3b
More robust scheme for untyped splices
odersky May 26, 2018
52ac21a
Make TreePickler untpd friendly
odersky May 28, 2018
444ce46
More debug options for printing
odersky May 29, 2018
1f33e9d
Add untyped types to Tasty
odersky May 29, 2018
2a2cb91
Tweaks to pickling and unpickling untyped trees
odersky May 29, 2018
798fbf7
Fix pickler printing of TERMREFin, TYPEREFin
odersky May 30, 2018
fc60ae9
Fix pickling of flags in untyped mode
odersky May 30, 2018
1a257ba
Add transparent modifier
odersky Jun 3, 2018
e95a000
Fixes to pickling and unpickling untyped trees
odersky Jun 3, 2018
5056b88
Prepare to have both inlined and transparent methods
odersky Jun 3, 2018
6762e68
Dual purpose inliner
odersky Jun 3, 2018
3617860
Two fixes to transparent handling
odersky Jun 3, 2018
89f3f93
Pretype nested transparent applications
odersky Jun 4, 2018
c70c879
Pretype nested transparent applications
odersky Jun 4, 2018
51139f0
Fix wrong owner when reading annotations.
odersky Jun 4, 2018
8d7936e
Expand test
odersky Jun 4, 2018
9e093c8
More tests
odersky Jun 5, 2018
c6616fd
Fix rebase breakage
odersky Jun 15, 2018
fdbf083
Better error position when inline call limit exceeded
odersky Jun 16, 2018
a9e9b29
Recursive transparent calls inline only in redex position
odersky Jun 16, 2018
add85ea
Fix test
odersky Jun 17, 2018
d4d6624
Reduce projections of data type fields in inliner
odersky Jun 18, 2018
8128902
Check baseclasses when determining purity of class
odersky Jun 18, 2018
244250e
Replace inlined pure constant expressions by literals
odersky Jun 19, 2018
86517af
Add neg test
odersky Jun 19, 2018
a736ea9
Allow to project on private fields
odersky Jun 20, 2018
8e5c8b4
Don't forget bindings in projections.
odersky Jun 20, 2018
f5a3b2e
Inline transparent methods even if result type does not match
odersky Jun 21, 2018
fe8870e
Don't constrain result type of inlineable transparent methods
odersky Jun 21, 2018
c36911e
Remove test
odersky Jun 21, 2018
809ab6e
Take parents of class into account when determining NoInits flags
odersky Jun 23, 2018
7f08890
Fix ErrorMessagesTest
odersky Jun 23, 2018
be53b5b
Don't drop impure arguments when reducing projections
odersky Jun 23, 2018
bf290ca
Fix: Don't drop impure arguments when reducing projections
odersky Jun 23, 2018
bccac5e
Update check file
odersky Jun 23, 2018
a60d1c2
Exclude memebrs of Predef from implicit inline environment
odersky Jun 25, 2018
43f276c
Allow types applied to terms in syntax and parser
odersky Jun 26, 2018
f64241c
Syntax for computed type definitions
odersky Jun 26, 2018
06dbeae
Drop unused HigherKinded flag
odersky Jun 27, 2018
81950ef
Coomputed type defs have a TypeBounds as declared result type.
odersky Jun 27, 2018
c9f7535
Add HKTermLambdas
odersky Jun 28, 2018
34ac63c
Make `Transparent` a common flag
odersky Jun 28, 2018
d5619ae
Make Term Lambda parameters distribute into bounds
odersky Jun 28, 2018
de2b06a
First stab at type methods
odersky Jun 28, 2018
0ec4d98
Implement pickling and unpickling of computed types
odersky Jun 29, 2018
486f412
Delete ErrorMessageTest
odersky Jun 29, 2018
55bac27
Fixes to allow for generic transparent type devfinitions
odersky Jun 29, 2018
6be4781
Make Apply a Term- or Type-Tree
odersky Jun 29, 2018
ca30494
Make hole reading context-independent
odersky Jun 29, 2018
bc73cd1
Tasty: Have a tpt form for Apply as well as TypeApply
odersky Jun 30, 2018
96c1d22
Drop AppliedTypeTree
odersky Jun 30, 2018
9a545e5
Adapt Namer.checkFlags to type defdefs
odersky Jun 30, 2018
85945b0
Make `toLambda` not force too much
odersky Jul 1, 2018
703edea
Handle HKTermLambdas in TypeComparer
odersky Jun 30, 2018
e965128
Handle HKTermLambdas in Printer
odersky Jun 30, 2018
ff14afa
Handle HKTermLambdas in Tasty and TastyImpl
odersky Jun 30, 2018
cf82715
Disallow curried type methods
odersky Jun 30, 2018
6e43b8a
Fix printing of method types
odersky Jul 1, 2018
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
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ object desugar {
case (vparam :: vparams) :: vparamss1 =>
def defaultGetter: DefDef =
DefDef(
name = DefaultGetterName(meth.name, n),
name = DefaultGetterName(meth.name.asTermName, n),
tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))),
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam), n),
tpt = TypeTree(),
Expand Down Expand Up @@ -357,7 +357,7 @@ object desugar {
val classTycon: Tree = new TypeRefTree // watching is set at end of method

def appliedTypeTree(tycon: Tree, args: List[Tree]) =
(if (args.isEmpty) tycon else AppliedTypeTree(tycon, args))
(if (args.isEmpty) tycon else TypeApply(tycon, args))
.withPos(cdef.pos.startPos)

def isHK(tparam: Tree): Boolean = tparam match {
Expand All @@ -371,7 +371,7 @@ object desugar {
val targ = refOfDef(tparam)
def fullyApplied(tparam: Tree): Tree = tparam match {
case TypeDef(_, LambdaTypeTree(tparams, body)) =>
AppliedTypeTree(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree)))
TypeApply(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree)))
case TypeDef(_, rhs: DerivedTypeTree) =>
fullyApplied(rhs.watched)
case _ =>
Expand Down Expand Up @@ -1113,7 +1113,7 @@ object desugar {
Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems)
case InfixOp(l, op, r) =>
if (ctx.mode is Mode.Type)
AppliedTypeTree(op, l :: r :: Nil) // op[l, r]
TypeApply(op, l :: r :: Nil) // op[l, r]
else {
assert(ctx.mode is Mode.Pattern) // expressions are handled separately by `binop`
Apply(op, l :: r :: Nil) // op(l, r)
Expand All @@ -1122,7 +1122,7 @@ object desugar {
if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == tpnme.raw.STAR) {
val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType
Annotated(
AppliedTypeTree(ref(seqType), t),
TypeApply(ref(seqType), t :: Nil),
New(ref(defn.RepeatedAnnotType), Nil :: Nil))
} else {
assert(ctx.mode.isExpr || ctx.reporter.hasErrors || ctx.mode.is(Mode.Interactive), ctx.mode)
Expand All @@ -1138,7 +1138,7 @@ object desugar {
ctx.error(TupleTooLong(ts), tree.pos)
unitLiteral
} else if (arity == 1) ts.head
else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts)
else if (ctx.mode is Mode.Type) TypeApply(ref(tupleTypeRef), ts)
else if (arity == 0) unitLiteral
else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts)
case WhileDo(cond, body) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ object DesugarEnums {

def extractType(t: Tree): Tree = t match {
case Apply(t1, _) => extractType(t1)
case TypeApply(t1, ts) => AppliedTypeTree(extractType(t1), ts)
case TypeApply(t1, ts) => TypeApply(extractType(t1), ts)
case Select(t1, nme.CONSTRUCTOR) => extractType(t1)
case New(t1) => t1
case t1 => t1
Expand Down
83 changes: 59 additions & 24 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
*/
def methPart(tree: Tree): Tree = stripApply(tree) match {
case TypeApply(fn, _) => methPart(fn)
case AppliedTypeTree(fn, _) => methPart(fn) // !!! should not be needed
case Block(stats, expr) => methPart(expr)
case mp => mp
}
Expand Down Expand Up @@ -152,7 +151,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match {
case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1)
case tpt: TypeTree => tpt.typeOpt.isRepeatedParam
case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
case TypeApply(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
case _ => false
}

Expand All @@ -164,7 +163,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2)
case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2)
case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind])
case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind])
case TypeApply(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind])
case Select(tpt, _) => mayBeTypePat(tpt)
case Annotated(tpt, _) => mayBeTypePat(tpt)
case _ => false
Expand Down Expand Up @@ -240,6 +239,35 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case y => y
}

/** The largest subset of {NoInits, PureInterface} that a
* trait or class enclosing this statement can have as flags.
*/
def defKind(tree: Tree)(implicit ctx: Context): FlagSet = unsplice(tree) match {
case EmptyTree | _: Import => NoInitsInterface
case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
case tree: DefDef =>
if (tree.unforcedRhs == EmptyTree &&
tree.vparamss.forall(_.forall(_.rhs.isEmpty))) NoInitsInterface
else NoInits
case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
case _ => EmptyFlags
}

/** The largest subset of {NoInits, PureInterface} that a
* trait or class with these parents can have as flags.
*/
def parentsKind(parents: List[Tree])(implicit ctx: Context): FlagSet = parents match {
case Nil => NoInitsInterface
case Apply(_, _ :: _) :: _ => EmptyFlags
case _ :: parents1 => parentsKind(parents1)
}

/** The largest subset of {NoInits, PureInterface} that a
* trait or class with this body can have as flags.
*/
def bodyKind(body: List[Tree])(implicit ctx: Context): FlagSet =
(NoInitsInterface /: body)((fs, stat) => fs & defKind(stat))

/** Checks whether predicate `p` is true for all result parts of this expression,
* where we zoom into Ifs, Matches, and Blocks.
*/
Expand All @@ -262,6 +290,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
case _ => tree
}

def isBounds(tree: Tree)(implicit ctx: Context) = tree match {
case tree: TypeBoundsTree => true
case TypedSplice(tree1) => tree1.tpe.isInstanceOf[TypeBounds]
case _ => false
}

/** True iff definition is a val or def with no right-hand-side, or it
* is an abstract typoe declaration
*/
Expand Down Expand Up @@ -310,6 +344,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
case _ => false
}

/** Is this the RHS of a transparnt type def, which needs to be represented as a DefDef? */
def isTypeDefRHS(tree: Tree): Boolean = tree match {
case tree: If => true
case _ => false
}

// todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
}

Expand Down Expand Up @@ -358,6 +398,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
refPurity(tree)
case Select(qual, _) =>
refPurity(tree).min(exprPurity(qual))
case New(_) =>
SimplyPure
case TypeApply(fn, _) =>
exprPurity(fn)
/*
Expand All @@ -369,13 +411,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case Apply(fn, args) =>
def isKnownPureOp(sym: Symbol) =
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
// Note: After uncurry, field accesses are represented as Apply(getter, Nil),
// so an Apply can also be pure.
if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn)
else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol))
// A constant expression with pure arguments is pure.
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)
// A constant expression with pure arguments is pure.
|| fn.symbol.isStable)
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
else Impure
else
Impure
case Typed(expr, _) =>
exprPurity(expr)
case Block(stats, expr) =>
Expand All @@ -402,11 +443,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
* @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
* flags set.
*/
def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel =
if (!tree.tpe.widen.isParameterless || tree.symbol.is(Erased)) SimplyPure
else if (!tree.symbol.isStable) Impure
else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start.
def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = {
val sym = tree.symbol
if (!tree.hasType) Impure
else if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure
else if (!sym.isStable) Impure
else if (sym.is(Module))
if (sym.moduleClass.isNoInitsClass) Pure else Idempotent
else if (sym.is(Lazy)) Idempotent
else SimplyPure
}

def isPureRef(tree: Tree)(implicit ctx: Context) =
refPurity(tree) == SimplyPure
Expand Down Expand Up @@ -623,17 +669,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
accum(Nil, root)
}

/** The largest subset of {NoInits, PureInterface} that a
* trait enclosing this statement can have as flags.
*/
def defKind(tree: Tree): FlagSet = unsplice(tree) match {
case EmptyTree | _: Import => NoInitsInterface
case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
case _ => EmptyFlags
}

/** The top level classes in this tree, including only those module classes that
* are not a linked class of some other class in the result.
*/
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole

/** A map that applies three functions and a substitution together to a tree and
* makes sure they are coordinated so that the result is well-typed. The functions are
* @param typeMap A function from Type to Type that gets applied to the
* @param typeMap A function from Type to Type that gets applied to the
* type of every tree node and to all locally defined symbols,
* followed by the substitution [substFrom := substTo].
* @param treeMap A transformer that translates all encountered subtrees in
Expand Down Expand Up @@ -95,7 +95,9 @@ class TreeTypeMap(
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
res.symbol.transformAnnotations {
case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs)
case ann: BodyAnnotation =>
if (res.symbol.isTransparentMethod) ann.derivedAnnotation(transform(ann.tree))
else ann.derivedAnnotation(res.rhs)
case ann => ann
}
res
Expand Down Expand Up @@ -126,7 +128,7 @@ class TreeTypeMap(
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) =
transformDefs(trees)._2

private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
val tmap = withMappedSyms(tpd.localSyms(trees))
(tmap, tmap.transformSub(trees))
}
Expand Down
Loading