From 1fa329ad1cfdc797480dc564e3919dfeee707206 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Wed, 6 Jun 2018 13:39:54 +0200 Subject: [PATCH 01/36] Add `TypeOf` type representation and corresponding case in `TypeMap` --- .../src/dotty/tools/dotc/core/Types.scala | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b90a5bde2c98..64f0fc120e49 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3929,6 +3929,21 @@ object Types { else None } + // ----- TypeOf ------------------------------------------------------------------------- + + case class TypeOf(tree: Tree, underlyingTp: Type) extends UncachedProxyType /* with SingletonType */ { + def underlying(implicit ctx: Context) = underlyingTp + + def equals(that: Type): Boolean = that match { + case that: TypeOf => this eq that + case _ => false + } + + def derivedTypeOf(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = + if ((this.tree eq tree) && (this.underlyingTp eq underlyingTp)) this + else TypeOf(tree, underlyingTp) + } + // ----- TypeMaps -------------------------------------------------------------------- /** Common base class of TypeMap and TypeAccumulator */ @@ -3982,6 +3997,8 @@ object Types { // note: currying needed because Scala2 does not support param-dependencies protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = tp.derivedLambdaType(tp.paramNames, formals, restpe) + protected def derivedTypeOf(tp: TypeOf, tree: Tree, underlyingTp: Type): Type = + tp.derivedTypeOf(tree, underlyingTp) /** Map this function over given type */ def mapOver(tp: Type): Type = { @@ -4074,6 +4091,20 @@ object Types { if (underlying1 eq underlying) tp else derivedAnnotatedType(tp, underlying1, mapOver(annot)) + case tp: TypeOf => + // TODO: Don't clone if type is unchanged + def copyMapped(tree: Tree): Tree = tree.clone.withTypeUnchecked(this(tree.tpe)) + val tree1 = tp.tree match { + case tree: Apply => + cpy.Apply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) + case tree: If => + cpy.If(tree)(copyMapped(tree.cond), copyMapped(tree.thenp), copyMapped(tree.elsep)) + case tree: Match => + ??? + case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") + } + derivedTypeOf(tp, tree1, this(tp.underlyingTp)) + case tp: WildcardType => derivedWildcardType(tp, mapOver(tp.optBounds)) @@ -4352,6 +4383,10 @@ object Types { tp.derivedLambdaType(tp.paramNames, formals, restpe) } + // TODO: Implement derivedTypeOf in ApproximatingTypeMap +// override protected def derivedTypeOf(tp: TypeOf, tree: Tree, underlyingTp: Type): Type = +// tp.derivedTypeOf(tree, underlyingTp) + protected def reapply(tp: Type): Type = apply(tp) } From cad6a10413fe8e3c03cf22217ff22e9112142f76 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 6 Jun 2018 14:57:54 +0200 Subject: [PATCH 02/36] Add TypeOfTypeTree and update Parser accordingly --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 15 +++++++++++++++ compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 +++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + compiler/src/dotty/tools/dotc/core/Types.scala | 8 ++++++++ .../dotty/tools/dotc/core/tasty/TreePickler.scala | 1 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 1 + .../core/unpickleScala2/Scala2Unpickler.scala | 2 ++ .../src/dotty/tools/dotc/parsing/Parsers.scala | 8 +++++++- .../tools/dotc/printing/RefinedPrinter.scala | 1 + .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 13 +++++++++++++ docs/docs/internals/syntax.md | 2 ++ 12 files changed, 60 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 9f72feb216b9..611c4fd468fa 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -611,6 +611,12 @@ object Trees { type ThisTree[-T >: Untyped] = SingletonTypeTree[T] } + /** { expr1 }, aka TypeOf type */ + case class TypeOfTypeTree[-T >: Untyped] private[ast] (ref: Tree[T]) + extends DenotingTree[T] with TypTree[T] { + type ThisTree[-T >: Untyped] = TypeOfTypeTree[T] + } + /** left & right */ case class AndTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T]) extends TypTree[T] { @@ -888,6 +894,7 @@ object Trees { type Inlined = Trees.Inlined[T] type TypeTree = Trees.TypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] + type TypeOfTypeTree = Trees.TypeOfTypeTree[T] type AndTypeTree = Trees.AndTypeTree[T] type OrTypeTree = Trees.OrTypeTree[T] type RefinedTypeTree = Trees.RefinedTypeTree[T] @@ -1041,6 +1048,10 @@ object Trees { case tree: SingletonTypeTree if ref eq tree.ref => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)) } + def TypeOfTypeTree(tree: Tree)(ref: Tree): TypeOfTypeTree = tree match { + case tree: TypeOfTypeTree if ref eq tree.ref => tree + case _ => finalize(tree, untpd.TypeOfTypeTree(ref)) + } def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match { case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.AndTypeTree(left, right)) @@ -1200,6 +1211,8 @@ object Trees { tree case SingletonTypeTree(ref) => cpy.SingletonTypeTree(tree)(transform(ref)) + case TypeOfTypeTree(ref) => + cpy.TypeOfTypeTree(tree)(transform(ref)) case AndTypeTree(left, right) => cpy.AndTypeTree(tree)(transform(left), transform(right)) case OrTypeTree(left, right) => @@ -1315,6 +1328,8 @@ object Trees { x case SingletonTypeTree(ref) => this(x, ref) + case TypeOfTypeTree(ref) => + this(x, ref) case AndTypeTree(left, right) => this(this(x, left), right) case OrTypeTree(left, right) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a9feeae2da99..5e75007b87b3 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -137,6 +137,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree = ta.assignType(untpd.SingletonTypeTree(ref), ref) + def TypeOfTypeTree(ref: Tree)(implicit ctx: Context): TypeOfTypeTree = + ta.assignType(untpd.TypeOfTypeTree(ref), ref) + def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree = ta.assignType(untpd.AndTypeTree(left, right), left, right) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 80e4365a943c..7e1f1677bde5 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -283,6 +283,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree): Inlined = new Inlined(call, bindings, expansion) def TypeTree() = new TypeTree() def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref) + def TypeOfTypeTree(ref: Tree): TypeOfTypeTree = new TypeOfTypeTree(ref) def AndTypeTree(left: Tree, right: Tree): AndTypeTree = new AndTypeTree(left, right) def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right) def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 64f0fc120e49..60b68c6ce1ad 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4099,6 +4099,14 @@ object Types { cpy.Apply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) case tree: If => cpy.If(tree)(copyMapped(tree.cond), copyMapped(tree.thenp), copyMapped(tree.elsep)) + case tree: Ident => + copyMapped(tree) + case tree: Select => + cpy.Select(tree)(copyMapped(tree.qualifier), tree.name) + case tree: Literal => + copyMapped(tree) + case tree: Block => + copyMapped(tree) case tree: Match => ??? case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index c2b22ade4ed6..8dd4ead841cc 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -513,6 +513,7 @@ class TreePickler(pickler: TastyPickler) { case SingletonTypeTree(ref) => writeByte(SINGLETONtpt) pickleTree(ref) + // TODO: case TypeOfTypeTree case RefinedTypeTree(parent, refinements) => if (refinements.isEmpty) pickleTree(parent) else { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ce955cee96c3..e807faa8e950 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1001,6 +1001,7 @@ class TreeUnpickler(reader: TastyReader, Throw(readTerm()) case SINGLETONtpt => SingletonTypeTree(readTerm()) + // TODO: case TypeOfTypeTree => case BYNAMEtpt => ByNameTypeTree(readTpt()) case NAMEDARG => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f7dbda218e0e..b099b4d3db02 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -1198,6 +1198,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case SINGLETONTYPEtree => SingletonTypeTree(readTreeRef()) + // TODO case TypeOfTypeTree => + case SELECTFROMTYPEtree => val qualifier = readTreeRef() val selector = readTypeNameRef() diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9ffa8cf9c52e..68c043e5bc6b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -870,7 +870,7 @@ object Parsers { makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) } else if (in.token == LBRACE) - atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) } + atPos(in.offset) { inBraces(refinementOnEmptyOrTypeOf()) } else if (isSimpleLiteral) { SingletonTypeTree(literal()) } else if (in.token == USCORE) { val start = in.skipToken() @@ -884,6 +884,12 @@ object Parsers { } } + /** A refinement on an empty tree or a TypeOf type tree. */ + def refinementOnEmptyOrTypeOf(): Tree = { + if (!isStatSeqEnd && !isDclIntro) TypeOfTypeTree(expr1()) + else RefinedTypeTree(EmptyTree, refineStatSeq()) + } + val handleSingletonType: Tree => Tree = t => if (in.token == TYPE) { in.nextToken() diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f1cf87ad9c1f..c704e1575920 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -373,6 +373,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { typeText(toText(tree.typeOpt)) case SingletonTypeTree(ref) => toTextLocal(ref) ~ "." ~ keywordStr("type") + // TODO case TypeOfTypeTree => case AndTypeTree(l, r) => changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) } case OrTypeTree(l, r) => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index afc4e83439e1..d4972cb54e65 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -484,6 +484,12 @@ trait TypeAssigner { def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = tree.withType(ref.tpe) + def assignType(tree: untpd.TypeOfTypeTree, ref: Tree)(implicit ctx: Context) = { + val typeOf = TypeOf(ref, ref.tpe) + ref.withTypeUnchecked(typeOf) + tree.withType(typeOf) + } + def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = tree.withType(AndType(left.tpe, right.tpe)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fa48b6315a63..47bc828b7311 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1175,6 +1175,18 @@ class Typer extends Namer assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } + def typedTypeOfTypeTree(tree: untpd.TypeOfTypeTree)(implicit ctx: Context): TypeOfTypeTree = track("typedTypeOfTypeTree") { + val ref1 = typedExpr(tree.ref) + ref1 match { + case _: Apply | _: If | _: Match | _: Block | _: Ident | _: Select | _: Literal => + // Ident, Select & Literal are also available in the alternative syntax, + // that is a.type, a.b.type, 1. For now we treat them differently + // depending on the syntax. + case tree => ctx.error(s"$tree is not a valid singleton type.", ref1.pos) + } + assignType(cpy.TypeOfTypeTree(tree)(ref1), ref1) + } + def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { val left1 = checkSimpleKinded(typed(tree.left)) val right1 = checkSimpleKinded(typed(tree.right)) @@ -1810,6 +1822,7 @@ class Typer extends Namer case tree: untpd.Inlined => typedInlined(tree, pt) case tree: untpd.TypeTree => typedTypeTree(tree, pt) case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree) + case tree: untpd.TypeOfTypeTree => typedTypeOfTypeTree(tree) case tree: untpd.AndTypeTree => typedAndTypeTree(tree) case tree: untpd.OrTypeTree => typedOrTypeTree(tree) case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index f275cbcd497d..498c0ebe3d0d 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -137,6 +137,7 @@ SimpleType ::= SimpleType TypeArgs | ‘(’ ArgTypes ‘)’ Tuple(ts) | ‘_’ TypeBounds | Refinement RefinedTypeTree(EmptyTree, refinement) + | TypeOf TypeOfTypeTree(expr) | SimpleLiteral SingletonTypeTree(l) ArgTypes ::= Type {‘,’ Type} | NamedTypeArg {‘,’ NamedTypeArg} @@ -148,6 +149,7 @@ TypeArgs ::= ‘[’ ArgTypes ‘]’ NamedTypeArg ::= id ‘=’ Type NamedArg(id, t) NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’ nts Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds +TypeOf ::= ‘{’ Expr1 ‘}’ expr TypeBounds ::= [‘>:’ Type] [‘<:’ Type] | INT TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {‘<%’ Type} {‘:’ Type} ContextBounds(typeBounds, tps) ``` From 42eed69f156ada11e35952ebdc27bc92719078ae Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Wed, 13 Jun 2018 18:38:57 +0200 Subject: [PATCH 03/36] Merge TypeOfTypeTree and SingletonTypeTree Disable stability check on SingletonTypeTree for now. Introduce TypeOf only to suspend type checking of top-level TypeApply, Apply, If and Match AST nodes. --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 17 +------------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 - .../src/dotty/tools/dotc/core/Types.scala | 23 ++++++++----------- .../tools/dotc/core/tasty/TreePickler.scala | 1 - .../tools/dotc/core/tasty/TreeUnpickler.scala | 1 - .../core/unpickleScala2/Scala2Unpickler.scala | 2 -- .../dotty/tools/dotc/parsing/Parsers.scala | 8 +++---- .../tools/dotc/printing/PlainPrinter.scala | 2 ++ .../tools/dotc/printing/RefinedPrinter.scala | 3 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 14 +++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 16 ++----------- 12 files changed, 27 insertions(+), 64 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 611c4fd468fa..c34ef32ca9cf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -605,18 +605,12 @@ object Trees { */ class TypeVarBinder[-T >: Untyped] extends TypeTree[T] - /** ref.type */ + /** ref.type or { ref } */ case class SingletonTypeTree[-T >: Untyped] private[ast] (ref: Tree[T]) extends DenotingTree[T] with TypTree[T] { type ThisTree[-T >: Untyped] = SingletonTypeTree[T] } - /** { expr1 }, aka TypeOf type */ - case class TypeOfTypeTree[-T >: Untyped] private[ast] (ref: Tree[T]) - extends DenotingTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = TypeOfTypeTree[T] - } - /** left & right */ case class AndTypeTree[-T >: Untyped] private[ast] (left: Tree[T], right: Tree[T]) extends TypTree[T] { @@ -894,7 +888,6 @@ object Trees { type Inlined = Trees.Inlined[T] type TypeTree = Trees.TypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] - type TypeOfTypeTree = Trees.TypeOfTypeTree[T] type AndTypeTree = Trees.AndTypeTree[T] type OrTypeTree = Trees.OrTypeTree[T] type RefinedTypeTree = Trees.RefinedTypeTree[T] @@ -1048,10 +1041,6 @@ object Trees { case tree: SingletonTypeTree if ref eq tree.ref => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)) } - def TypeOfTypeTree(tree: Tree)(ref: Tree): TypeOfTypeTree = tree match { - case tree: TypeOfTypeTree if ref eq tree.ref => tree - case _ => finalize(tree, untpd.TypeOfTypeTree(ref)) - } def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match { case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.AndTypeTree(left, right)) @@ -1211,8 +1200,6 @@ object Trees { tree case SingletonTypeTree(ref) => cpy.SingletonTypeTree(tree)(transform(ref)) - case TypeOfTypeTree(ref) => - cpy.TypeOfTypeTree(tree)(transform(ref)) case AndTypeTree(left, right) => cpy.AndTypeTree(tree)(transform(left), transform(right)) case OrTypeTree(left, right) => @@ -1328,8 +1315,6 @@ object Trees { x case SingletonTypeTree(ref) => this(x, ref) - case TypeOfTypeTree(ref) => - this(x, ref) case AndTypeTree(left, right) => this(this(x, left), right) case OrTypeTree(left, right) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 5e75007b87b3..a9feeae2da99 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -137,9 +137,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree = ta.assignType(untpd.SingletonTypeTree(ref), ref) - def TypeOfTypeTree(ref: Tree)(implicit ctx: Context): TypeOfTypeTree = - ta.assignType(untpd.TypeOfTypeTree(ref), ref) - def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree = ta.assignType(untpd.AndTypeTree(left, right), left, right) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 7e1f1677bde5..80e4365a943c 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -283,7 +283,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree): Inlined = new Inlined(call, bindings, expansion) def TypeTree() = new TypeTree() def SingletonTypeTree(ref: Tree): SingletonTypeTree = new SingletonTypeTree(ref) - def TypeOfTypeTree(ref: Tree): TypeOfTypeTree = new TypeOfTypeTree(ref) def AndTypeTree(left: Tree, right: Tree): AndTypeTree = new AndTypeTree(left, right) def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right) def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 60b68c6ce1ad..36afec5c47e5 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3931,7 +3931,7 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- - case class TypeOf(tree: Tree, underlyingTp: Type) extends UncachedProxyType /* with SingletonType */ { + case class TypeOf(tree: Tree, underlyingTp: Type) extends UncachedProxyType with SingletonType { def underlying(implicit ctx: Context) = underlyingTp def equals(that: Type): Boolean = that match { @@ -4092,24 +4092,21 @@ object Types { else derivedAnnotatedType(tp, underlying1, mapOver(annot)) case tp: TypeOf => - // TODO: Don't clone if type is unchanged - def copyMapped(tree: Tree): Tree = tree.clone.withTypeUnchecked(this(tree.tpe)) + def copyMapped[ThisTree <: Tree](tree: ThisTree): ThisTree = { + val tp1 = this(tree.tpe) + if (tp eq tp1) tree else tree.withTypeUnchecked(tp1).asInstanceOf[ThisTree] + } val tree1 = tp.tree match { + case tree: TypeApply => + cpy.TypeApply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) case tree: Apply => cpy.Apply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) case tree: If => cpy.If(tree)(copyMapped(tree.cond), copyMapped(tree.thenp), copyMapped(tree.elsep)) - case tree: Ident => - copyMapped(tree) - case tree: Select => - cpy.Select(tree)(copyMapped(tree.qualifier), tree.name) - case tree: Literal => - copyMapped(tree) - case tree: Block => - copyMapped(tree) case tree: Match => - ??? - case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") + cpy.Match(tree)(copyMapped(tree.selector), tree.cases.mapConserve(copyMapped)) + case tree => + throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } derivedTypeOf(tp, tree1, this(tp.underlyingTp)) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8dd4ead841cc..c2b22ade4ed6 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -513,7 +513,6 @@ class TreePickler(pickler: TastyPickler) { case SingletonTypeTree(ref) => writeByte(SINGLETONtpt) pickleTree(ref) - // TODO: case TypeOfTypeTree case RefinedTypeTree(parent, refinements) => if (refinements.isEmpty) pickleTree(parent) else { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e807faa8e950..ce955cee96c3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1001,7 +1001,6 @@ class TreeUnpickler(reader: TastyReader, Throw(readTerm()) case SINGLETONtpt => SingletonTypeTree(readTerm()) - // TODO: case TypeOfTypeTree => case BYNAMEtpt => ByNameTypeTree(readTpt()) case NAMEDARG => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index b099b4d3db02..f7dbda218e0e 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -1198,8 +1198,6 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case SINGLETONTYPEtree => SingletonTypeTree(readTreeRef()) - // TODO case TypeOfTypeTree => - case SELECTFROMTYPEtree => val qualifier = readTreeRef() val selector = readTypeNameRef() diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 68c043e5bc6b..193c9625f6e6 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -870,7 +870,7 @@ object Parsers { makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true))) } else if (in.token == LBRACE) - atPos(in.offset) { inBraces(refinementOnEmptyOrTypeOf()) } + atPos(in.offset) { inBraces(refinementOnEmptyOrSingleton()) } else if (isSimpleLiteral) { SingletonTypeTree(literal()) } else if (in.token == USCORE) { val start = in.skipToken() @@ -884,9 +884,9 @@ object Parsers { } } - /** A refinement on an empty tree or a TypeOf type tree. */ - def refinementOnEmptyOrTypeOf(): Tree = { - if (!isStatSeqEnd && !isDclIntro) TypeOfTypeTree(expr1()) + /** A refinement on an empty tree or a singleton type tree. */ + def refinementOnEmptyOrSingleton(): Tree = { + if (!isStatSeqEnd && !isDclIntro) SingletonTypeTree(expr1()) else RefinedTypeTree(EmptyTree, refineStatSeq()) } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 79c00e0f9f92..359ce2abaa9e 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -141,6 +141,8 @@ class PlainPrinter(_ctx: Context) extends Printer { ParamRefNameString(tp) ~ ".type" case tp: TypeParamRef => ParamRefNameString(tp) ~ lambdaHash(tp.binder) + case tp: TypeOf => + "{ " ~ toTextLocal(tp.tree) ~ " }" case tp: SingletonType => toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" case AppliedType(tycon, args) => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c704e1575920..abd8bb4cb1e5 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -372,8 +372,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case TypeTree() => typeText(toText(tree.typeOpt)) case SingletonTypeTree(ref) => - toTextLocal(ref) ~ "." ~ keywordStr("type") - // TODO case TypeOfTypeTree => + "{ " ~ toTextLocal(ref) ~ " }" case AndTypeTree(l, r) => changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) } case OrTypeTree(l, r) => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index d4972cb54e65..db259f78b75e 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -481,13 +481,13 @@ trait TypeAssigner { tree.withType(ownType) } - def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = - tree.withType(ref.tpe) - - def assignType(tree: untpd.TypeOfTypeTree, ref: Tree)(implicit ctx: Context) = { - val typeOf = TypeOf(ref, ref.tpe) - ref.withTypeUnchecked(typeOf) - tree.withType(typeOf) + def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = { + val tp = ref match { + case _: TypeApply | _: Apply | _: If | _: Match => TypeOf(ref, ref.tpe) + case _: Literal | _: Ident | _: Select | _: Block => ref.tpe + case _ => throw new AssertionError(i"Tree $ref is not a valid reference for a singleton type tree.") + } + tree.withType(tp) } def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 47bc828b7311..84e7f2729a1a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1171,22 +1171,11 @@ class Typer extends Namer def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) - checkStable(ref1.tpe, tree.pos) + // TODO: Discuss stability requirements of singleton type trees and potentially reenable check + // checkStable(ref1.tpe, tree.pos) assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } - def typedTypeOfTypeTree(tree: untpd.TypeOfTypeTree)(implicit ctx: Context): TypeOfTypeTree = track("typedTypeOfTypeTree") { - val ref1 = typedExpr(tree.ref) - ref1 match { - case _: Apply | _: If | _: Match | _: Block | _: Ident | _: Select | _: Literal => - // Ident, Select & Literal are also available in the alternative syntax, - // that is a.type, a.b.type, 1. For now we treat them differently - // depending on the syntax. - case tree => ctx.error(s"$tree is not a valid singleton type.", ref1.pos) - } - assignType(cpy.TypeOfTypeTree(tree)(ref1), ref1) - } - def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { val left1 = checkSimpleKinded(typed(tree.left)) val right1 = checkSimpleKinded(typed(tree.right)) @@ -1822,7 +1811,6 @@ class Typer extends Namer case tree: untpd.Inlined => typedInlined(tree, pt) case tree: untpd.TypeTree => typedTypeTree(tree, pt) case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree) - case tree: untpd.TypeOfTypeTree => typedTypeOfTypeTree(tree) case tree: untpd.AndTypeTree => typedAndTypeTree(tree) case tree: untpd.OrTypeTree => typedOrTypeTree(tree) case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) From d61e3ed0dc46d8da65178a8a28565b33bc656326 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 6 Jun 2018 18:41:43 +0200 Subject: [PATCH 04/36] Handle TypeOf in pickler --- compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 4 +++- compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala | 2 ++ compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 3 +++ compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 1d659828abfe..3819fbaffb71 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -402,6 +402,7 @@ object TastyFormat { final val ANNOTATION = 172 final val TERMREFin = 173 final val TYPEREFin = 174 + final val TYPEOF = 175 // In binary: 101100EI // I = implicit method type @@ -430,7 +431,7 @@ object TastyFormat { firstNatTreeTag <= tag && tag <= SYMBOLconst || firstASTTreeTag <= tag && tag <= SINGLETONtpt || firstNatASTTreeTag <= tag && tag <= NAMEDARG || - firstLengthTreeTag <= tag && tag <= TYPEREFin || + firstLengthTreeTag <= tag && tag <= ERASEDIMPLICITMETHODtype || tag == HOLE def isParamTag(tag: Int) = tag == PARAM || tag == TYPEPARAM @@ -595,6 +596,7 @@ object TastyFormat { case SUPERtype => "SUPERtype" case TERMREFin => "TERMREFin" case TYPEREFin => "TYPEREFin" + case TYPEOF => "TYPEOF" case REFINEDtype => "REFINEDtype" case REFINEDtpt => "REFINEDtpt" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index c4ee8cffcf05..fea38b33aa62 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -74,6 +74,8 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { until(end) { printName(); printTree() } case PARAMtype => printNat(); printNat() + case TYPEOF => + printTree(); printTree() case _ => printTrees() } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index c2b22ade4ed6..342103cc89a6 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -159,6 +159,9 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => pickleConstant(value) + case tpe: TypeOf => + writeByte(TYPEOF) + withLength { pickleTree(tpe.tree); pickleType(tpe.underlyingTp, richTypes) } case tpe: NamedType => val sym = tpe.symbol def pickleExternalRef(sym: Symbol) = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ce955cee96c3..f215f0e86e13 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -324,6 +324,8 @@ class TreeUnpickler(reader: TastyReader, TypeBounds(readType(), readType()) case ANNOTATEDtype => AnnotatedType(readType(), Annotation(readTerm())) + case TYPEOF => + TypeOf(readTerm(), readType()) case ANDtype => AndType(readType(), readType()) case ORtype => From bbdbfb5a54dc35419338ccabc8e9c66637aeb518 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 18:40:10 +0200 Subject: [PATCH 05/36] Handle TypeOf in TypeAssigner --- compiler/src/dotty/tools/dotc/core/Types.scala | 9 +++++++++ .../dotty/tools/dotc/typer/TypeAssigner.scala | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 36afec5c47e5..fcbe757a45a5 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3944,6 +3944,15 @@ object Types { else TypeOf(tree, underlyingTp) } + object TypeOf { + /** To be used from type assigner. The assumption is that tree is currently + * being type assigned, and will be typed by the time it reaches the + * outside world. + */ + private[dotc] def fromUntyped(tree: untpd.Tree, tpe: Type): TypeOf = + TypeOf(tree.asInstanceOf[Tree], tpe) + } + // ----- TypeMaps -------------------------------------------------------------------- /** Common base class of TypeMap and TypeAccumulator */ diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index db259f78b75e..3f9cc2f4bcdb 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -364,10 +364,13 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case fntpe: MethodType => - if (sameLength(fntpe.paramInfos, args) || ctx.phase.prev.relaxedTyping) - if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes) - else fntpe.resultType - else + if (sameLength(fntpe.paramInfos, args) || ctx.phase.prev.relaxedTyping) { + val tpe = + if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes) + else fntpe.resultType + if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tree, tpe) + else tpe + } else errorType(i"wrong number of arguments at ${ctx.phase.prev} for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.pos) case t => errorType(err.takesNoParamsStr(fn, ""), tree.pos) @@ -424,7 +427,11 @@ trait TypeAssigner { } else { val argTypes = args.tpes - if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes) + if (sameLength(argTypes, paramNames)) { + val tpe = pt.instantiate(argTypes) + if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tree, tpe) + else tpe + } else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) } case err: ErrorType => From 75ace99481274f0d01e65a6203997865188fabbe Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Wed, 13 Jun 2018 18:41:24 +0200 Subject: [PATCH 06/36] Add structural equality for `TypeOf`s --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 ++ .../src/dotty/tools/dotc/core/Types.scala | 33 +++++++++++++++++-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 8 +++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 4efcb0648c12..9a0a7d8754b8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -238,6 +238,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { !tp2.evaluating && recur(tp1, tp2.ref) case tp2: AnnotatedType if !tp2.isRefining => recur(tp1, tp2.parent) + case tp2: TypeOf => + tp1 == tp2 || secondTry case tp2: ThisType => def compareThis = { val cls2 = tp2.cls diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index fcbe757a45a5..921a50506e21 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3932,11 +3932,33 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- case class TypeOf(tree: Tree, underlyingTp: Type) extends UncachedProxyType with SingletonType { + assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree: $tree") + assert(!underlyingTp.isInstanceOf[TypeOf]) + def underlying(implicit ctx: Context) = underlyingTp - def equals(that: Type): Boolean = that match { - case that: TypeOf => this eq that - case _ => false + override def equals(that: Any): Boolean = { + that match { + case that: TypeOf => + def compareTree(tree1: Tree, tree2: Tree): Boolean = { + def compareArgs[T <: Tree](args1: List[T], args2: List[T]): Boolean = + args1.zip(args2).forall { case (a,b) => a.tpe == b.tpe } + (tree1, tree2) match { + case (t1: Apply, t2: Apply) => + t1.fun.tpe == t2.fun.tpe && compareArgs(t1.args, t2.args) + case (t1: TypeApply, t2: TypeApply) => + t1.fun.tpe == t2.fun.tpe && compareArgs(t1.args, t2.args) + case (t1: If, t2: If) => + t1.cond.tpe == t2.cond.tpe && t1.thenp.tpe == t2.thenp.tpe && t1.elsep.tpe == t2.elsep.tpe + case (t1: Match, t2: Match) => + t1.selector.tpe == t2.selector.tpe && compareArgs(t1.cases, t2.cases) + case (t1, t2) => + false + } + } + compareTree(this.tree, that.tree) && { assert(this.underlyingTp == that.underlyingTp); true } + case _ => false + } } def derivedTypeOf(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = @@ -3951,6 +3973,11 @@ object Types { */ private[dotc] def fromUntyped(tree: untpd.Tree, tpe: Type): TypeOf = TypeOf(tree.asInstanceOf[Tree], tpe) + + private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { + case _: TypeApply | _: Apply | _: If | _: Match => true + case _ => false + } } // ----- TypeMaps -------------------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 3f9cc2f4bcdb..27bf4fcd82d8 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -490,9 +490,13 @@ trait TypeAssigner { def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = { val tp = ref match { - case _: TypeApply | _: Apply | _: If | _: Match => TypeOf(ref, ref.tpe) case _: Literal | _: Ident | _: Select | _: Block => ref.tpe - case _ => throw new AssertionError(i"Tree $ref is not a valid reference for a singleton type tree.") + case _ => + if (TypeOf.isLegalTopLevelTree(ref)) + if (ref.tpe.isInstanceOf[TypeOf]) ref.tpe + else TypeOf(ref, ref.tpe) + else + throw new AssertionError(i"Tree $ref is not a valid reference for a singleton type tree.") } tree.withType(tp) } From d188933d34940545c35fbfdb9b2f317511eece58 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 12 Jun 2018 13:22:42 +0200 Subject: [PATCH 07/36] Add missing case in sameTree --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c34ef32ca9cf..528392bf203d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -227,8 +227,9 @@ object Trees { case y: List[_] => x.corresponds(y)(isSame) case _ => false } + case x: Constant => x == y case _ => - false + throw new AssertionError(s"Unexpected Tree in Tree comparison $x (comparing to $y)") } } this.getClass == that.getClass && { From 9767a701ba1856f61865197a36c49eb8e80dbb7b Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 18:42:08 +0200 Subject: [PATCH 08/36] Detect transparency when typing if and match --- .../dotty/tools/dotc/core/SymDenotations.scala | 4 ++++ .../dotty/tools/dotc/typer/TypeAssigner.scala | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 95301176d78f..a5c26588f819 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -785,6 +785,10 @@ object SymDenotations { def isTransparentMethod(implicit ctx: Context): Boolean = is(TransparentMethod, butNot = Accessor) + /** Does this symbol have a transparent owner, itself included? */ + def isTransitivelyTransparent(implicit ctx: Context): Boolean = + ownersIterator.exists(_.isTransparentMethod) + def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod /** ()T and => T types should be treated as equivalent for this symbol. diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 27bf4fcd82d8..172b31296cff 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -459,8 +459,13 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = - tree.withType(thenp.tpe | elsep.tpe) + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { + val underlying = thenp.tpe | elsep.tpe + if (ctx.owner.isTransitivelyTransparent) + tree.withType(TypeOf.fromUntyped(tree, underlying)) + else + tree.withType(underlying) + } def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = tree.withType( @@ -470,8 +475,13 @@ trait TypeAssigner { def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = tree.withType(body.tpe) - def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = - tree.withType(ctx.typeComparer.lub(cases.tpes)) + def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { + val underlying = ctx.typeComparer.lub(cases.tpes) + if (ctx.owner.isTransitivelyTransparent) + tree.withType(TypeOf.fromUntyped(tree, underlying)) + else + tree.withType(underlying) + } def assignType(tree: untpd.Return)(implicit ctx: Context) = tree.withType(defn.NothingType) From 649aa98f8ba501878f6d57919689efb977d26b4e Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 11:36:27 +0200 Subject: [PATCH 09/36] Make TypeOf a subclass of AnnotatedType The reason why we need to ba a proper subtype of AnnotatedType and not simply instanciate it is because we a custom equality. For instance we want to disregard the differance between Ident and TypedTrees since the later replaces the former during typing. --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ compiler/src/dotty/tools/dotc/core/Types.scala | 18 +++++++++--------- .../src/scala/annotation/internal/TypeOf.scala | 9 +++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 library/src/scala/annotation/internal/TypeOf.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 49bb1e4eb26e..a51c20c8fffd 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -752,6 +752,8 @@ class Definitions { def ThrowsAnnot(implicit ctx: Context) = ThrowsAnnotType.symbol.asClass lazy val TransientAnnotType = ctx.requiredClassRef("scala.transient") def TransientAnnot(implicit ctx: Context) = TransientAnnotType.symbol.asClass + lazy val TypeOfAnnotType = ctx.requiredClassRef("scala.annotation.internal.TypeOf") + def TypeOfAnnot(implicit ctx: Context) = TypeOfAnnotType.symbol.asClass lazy val UncheckedAnnotType = ctx.requiredClassRef("scala.unchecked") def UncheckedAnnot(implicit ctx: Context) = UncheckedAnnotType.symbol.asClass lazy val UncheckedStableAnnotType = ctx.requiredClassRef("scala.annotation.unchecked.uncheckedStable") diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 921a50506e21..4d836fc448bc 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3931,12 +3931,10 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- - case class TypeOf(tree: Tree, underlyingTp: Type) extends UncachedProxyType with SingletonType { + class TypeOf(val tree: Tree, val underlyingTp: Type)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree: $tree") assert(!underlyingTp.isInstanceOf[TypeOf]) - def underlying(implicit ctx: Context) = underlyingTp - override def equals(that: Any): Boolean = { that match { case that: TypeOf => @@ -3967,11 +3965,13 @@ object Types { } object TypeOf { + def apply(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = new TypeOf(tree, underlyingTp) + /** To be used from type assigner. The assumption is that tree is currently * being type assigned, and will be typed by the time it reaches the * outside world. */ - private[dotc] def fromUntyped(tree: untpd.Tree, tpe: Type): TypeOf = + private[dotc] def fromUntyped(tree: untpd.Tree, tpe: Type)(implicit ctx: Context): TypeOf = TypeOf(tree.asInstanceOf[Tree], tpe) private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { @@ -4122,11 +4122,6 @@ object Types { case tp: SkolemType => tp - case tp @ AnnotatedType(underlying, annot) => - val underlying1 = this(underlying) - if (underlying1 eq underlying) tp - else derivedAnnotatedType(tp, underlying1, mapOver(annot)) - case tp: TypeOf => def copyMapped[ThisTree <: Tree](tree: ThisTree): ThisTree = { val tp1 = this(tree.tpe) @@ -4146,6 +4141,11 @@ object Types { } derivedTypeOf(tp, tree1, this(tp.underlyingTp)) + case tp @ AnnotatedType(underlying, annot) => + val underlying1 = this(underlying) + if (underlying1 eq underlying) tp + else derivedAnnotatedType(tp, underlying1, mapOver(annot)) + case tp: WildcardType => derivedWildcardType(tp, mapOver(tp.optBounds)) diff --git a/library/src/scala/annotation/internal/TypeOf.scala b/library/src/scala/annotation/internal/TypeOf.scala new file mode 100644 index 000000000000..c9307c26ffae --- /dev/null +++ b/library/src/scala/annotation/internal/TypeOf.scala @@ -0,0 +1,9 @@ +package scala.annotation.internal + +import scala.annotation.RefiningAnnotation + +/** An annotation used with AnnotatedTypes to capture a term-level expression's + * type precisely. + * @param tree A typed Tree. + */ +final class TypeOf(tree: Any) extends RefiningAnnotation From e55c92fc94568e0e16ae838ddaea85900d2d4ad3 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 11:51:02 +0200 Subject: [PATCH 10/36] Override derivedAnnotatedType --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 4d836fc448bc..279f4265e6b0 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3962,6 +3962,10 @@ object Types { def derivedTypeOf(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = if ((this.tree eq tree) && (this.underlyingTp eq underlyingTp)) this else TypeOf(tree, underlyingTp) + + override def derivedAnnotatedType(parent: Type, annot: Annotation): AnnotatedType = + if ((parent eq this.parent) && (annot eq this.annot)) this + else TypeOf(annot.arguments.head, parent) } object TypeOf { From d3161efa9bb33d8317c4587eb4cfbcc4b60cfd4c Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 11:54:16 +0200 Subject: [PATCH 11/36] Swap tree and underlying in TypeOf to match Annot --- .../src/dotty/tools/dotc/core/Types.scala | 20 +++++++++---------- .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 279f4265e6b0..00ae35d69e01 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3931,7 +3931,7 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- - class TypeOf(val tree: Tree, val underlyingTp: Type)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { + class TypeOf(val underlyingTp: Type, val tree: Tree)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree: $tree") assert(!underlyingTp.isInstanceOf[TypeOf]) @@ -3959,24 +3959,24 @@ object Types { } } - def derivedTypeOf(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = + def derivedTypeOf(underlyingTp: Type, tree: Tree)(implicit ctx: Context): TypeOf = if ((this.tree eq tree) && (this.underlyingTp eq underlyingTp)) this - else TypeOf(tree, underlyingTp) + else TypeOf(underlyingTp, tree) override def derivedAnnotatedType(parent: Type, annot: Annotation): AnnotatedType = if ((parent eq this.parent) && (annot eq this.annot)) this - else TypeOf(annot.arguments.head, parent) + else TypeOf(parent, annot.arguments.head) } object TypeOf { - def apply(tree: Tree, underlyingTp: Type)(implicit ctx: Context): TypeOf = new TypeOf(tree, underlyingTp) + def apply(underlyingTp: Type, tree: Tree)(implicit ctx: Context): TypeOf = new TypeOf(underlyingTp, tree) /** To be used from type assigner. The assumption is that tree is currently * being type assigned, and will be typed by the time it reaches the * outside world. */ - private[dotc] def fromUntyped(tree: untpd.Tree, tpe: Type)(implicit ctx: Context): TypeOf = - TypeOf(tree.asInstanceOf[Tree], tpe) + private[dotc] def fromUntyped(tpe: Type, tree: untpd.Tree)(implicit ctx: Context): TypeOf = + TypeOf(tpe, tree.asInstanceOf[Tree]) private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { case _: TypeApply | _: Apply | _: If | _: Match => true @@ -4037,8 +4037,8 @@ object Types { // note: currying needed because Scala2 does not support param-dependencies protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = tp.derivedLambdaType(tp.paramNames, formals, restpe) - protected def derivedTypeOf(tp: TypeOf, tree: Tree, underlyingTp: Type): Type = - tp.derivedTypeOf(tree, underlyingTp) + protected def derivedTypeOf(tp: TypeOf, underlyingTp: Type, tree: Tree): Type = + tp.derivedTypeOf(underlyingTp, tree) /** Map this function over given type */ def mapOver(tp: Type): Type = { @@ -4143,7 +4143,7 @@ object Types { case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } - derivedTypeOf(tp, tree1, this(tp.underlyingTp)) + derivedTypeOf(tp, this(tp.underlyingTp), tree1) case tp @ AnnotatedType(underlying, annot) => val underlying1 = this(underlying) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 342103cc89a6..95ca507cab49 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -161,7 +161,7 @@ class TreePickler(pickler: TastyPickler) { pickleConstant(value) case tpe: TypeOf => writeByte(TYPEOF) - withLength { pickleTree(tpe.tree); pickleType(tpe.underlyingTp, richTypes) } + withLength { pickleType(tpe.underlyingTp, richTypes); pickleTree(tpe.tree) } case tpe: NamedType => val sym = tpe.symbol def pickleExternalRef(sym: Symbol) = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index f215f0e86e13..f0e74ffedca4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -325,7 +325,7 @@ class TreeUnpickler(reader: TastyReader, case ANNOTATEDtype => AnnotatedType(readType(), Annotation(readTerm())) case TYPEOF => - TypeOf(readTerm(), readType()) + TypeOf(readType(), readTerm()) case ANDtype => AndType(readType(), readType()) case ORtype => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 172b31296cff..8e089458d8c9 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -368,7 +368,7 @@ trait TypeAssigner { val tpe = if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes) else fntpe.resultType - if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tree, tpe) + if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tpe, tree) else tpe } else errorType(i"wrong number of arguments at ${ctx.phase.prev} for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.pos) @@ -429,7 +429,7 @@ trait TypeAssigner { val argTypes = args.tpes if (sameLength(argTypes, paramNames)) { val tpe = pt.instantiate(argTypes) - if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tree, tpe) + if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tpe, tree) else tpe } else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) @@ -462,7 +462,7 @@ trait TypeAssigner { def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { val underlying = thenp.tpe | elsep.tpe if (ctx.owner.isTransitivelyTransparent) - tree.withType(TypeOf.fromUntyped(tree, underlying)) + tree.withType(TypeOf.fromUntyped(underlying, tree)) else tree.withType(underlying) } @@ -478,7 +478,7 @@ trait TypeAssigner { def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { val underlying = ctx.typeComparer.lub(cases.tpes) if (ctx.owner.isTransitivelyTransparent) - tree.withType(TypeOf.fromUntyped(tree, underlying)) + tree.withType(TypeOf.fromUntyped(underlying, tree)) else tree.withType(underlying) } @@ -504,7 +504,7 @@ trait TypeAssigner { case _ => if (TypeOf.isLegalTopLevelTree(ref)) if (ref.tpe.isInstanceOf[TypeOf]) ref.tpe - else TypeOf(ref, ref.tpe) + else TypeOf(ref.tpe, ref) else throw new AssertionError(i"Tree $ref is not a valid reference for a singleton type tree.") } From 91c72bf1c25101045ca6417f3c910b7ad48d0f9c Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 18:42:51 +0200 Subject: [PATCH 12/36] Decompose precise protos during typing --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 9 ++++++++- .../src/dotty/tools/dotc/printing/PlainPrinter.scala | 4 ++-- .../src/dotty/tools/dotc/transform/PostTyper.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 12 +++++++++--- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9a0a7d8754b8..0a91a8e36b26 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -236,10 +236,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareWild case tp2: LazyRef => !tp2.evaluating && recur(tp1, tp2.ref) + case tp2: TypeOf => + tp2 == tp1 || secondTry case tp2: AnnotatedType if !tp2.isRefining => recur(tp1, tp2.parent) - case tp2: TypeOf => - tp1 == tp2 || secondTry case tp2: ThisType => def compareThis = { val cls2 = tp2.cls diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 00ae35d69e01..3576172b234f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -21,7 +21,7 @@ import util.Stats._ import util.{DotClass, SimpleIdentitySet} import reporting.diagnostic.Message import ast.tpd._ -import ast.TreeTypeMap +import ast.{Trees, TreeTypeMap} import printing.Texts._ import ast.untpd import dotty.tools.dotc.transform.Erasure @@ -3982,6 +3982,13 @@ object Types { case _: TypeApply | _: Apply | _: If | _: Match => true case _ => false } + + object If { + def unapply(to: TypeOf): Option[(Type, Type, Type)] = to.tree match { + case Trees.If(cond, thenb, elseb) => Some((cond.tpe, thenb.tpe, elseb.tpe)) + case _ => None + } + } } // ----- TypeMaps -------------------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 359ce2abaa9e..ecf144c68c72 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -141,8 +141,6 @@ class PlainPrinter(_ctx: Context) extends Printer { ParamRefNameString(tp) ~ ".type" case tp: TypeParamRef => ParamRefNameString(tp) ~ lambdaHash(tp.binder) - case tp: TypeOf => - "{ " ~ toTextLocal(tp.tree) ~ " }" case tp: SingletonType => toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" case AppliedType(tycon, args) => @@ -185,6 +183,8 @@ class PlainPrinter(_ctx: Context) extends Printer { (Str(" => ") provided !tp.resultType.isInstanceOf[MethodType]) ~ toTextGlobal(tp.resultType) } + case tp: TypeOf => + "{ " ~ toTextLocal(tp.tree) ~ " }" case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) case tp: TypeVar => diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 918d85afeca6..03856f5ec6d5 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -273,7 +273,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: TypeTree => tree.withType( tree.tpe match { - case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot)) + case tpe: AnnotatedType => tpe.derivedAnnotatedType(tpe.parent, transformAnnot(tpe.annot)) case tpe => tpe } ) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 84e7f2729a1a..2cb59ad7b814 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -707,10 +707,16 @@ class Typer extends Namer } def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") { - val cond1 = typed(tree.cond, defn.BooleanType) + val (condProto, thenProto, elseProto) = + pt match { + case TypeOf.If(c, t ,e) => (c, t, e) + case _ => (defn.BooleanType, pt.notApplied, pt.notApplied) + } + + val cond1 = typed(tree.cond, condProto) val thenp2 :: elsep2 :: Nil = harmonic(harmonize) { - val thenp1 = typed(tree.thenp, pt.notApplied) - val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied) + val thenp1 = typed(tree.thenp, thenProto) + val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), elseProto) thenp1 :: elsep1 :: Nil } assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) From 8a9e6a1fadd3f93827c122bceae9d2eadd93bc4a Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Wed, 13 Jun 2018 16:49:19 +0200 Subject: [PATCH 13/36] Avoid infinite loop when printing TypeOf --- .../dotty/tools/dotc/printing/PlainPrinter.scala | 7 +++++-- .../tools/dotc/printing/RefinedPrinter.scala | 15 ++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index ecf144c68c72..15f0219e0e05 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -485,9 +485,12 @@ class PlainPrinter(_ctx: Context) extends Printer { val nodeName = tree.productPrefix val elems = Text(tree.productIterator.map(toTextElem).toList, ", ") - val tpSuffix = + val tpSuffix: Text = if (ctx.settings.XprintTypes.value && tree.hasType) - " | " ~ toText(tree.typeOpt) + tree.typeOpt match { + case tp: TypeOf if tp.tree.tpe eq tp => " | " + case tp => " | " ~ toText(tree.typeOpt) + } else Text() diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index abd8bb4cb1e5..520e1d06abb2 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -541,23 +541,24 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (ctx.settings.XprintTypes.value && tree.hasType) { // add type to term nodes; replace type nodes with their types unless -Yprint-pos is also set. - def tp = tree.typeOpt match { - case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying + def tpText: Text = tree.typeOpt match { + case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => toText(tp.underlying) case tp: ConstantType if homogenizedView => // constant folded types are forgotten in Tasty, are reconstituted subsequently in FirstTransform. // Therefore we have to gloss over this when comparing before/after pickling by widening to // underlying type `T`, or, if expression is a unary primitive operation, to `=> T`. - tree match { + toText(tree match { case Select(qual, _) if qual.typeOpt.widen.typeSymbol.isPrimitiveValueClass => ExprType(tp.widen) case _ => tp.widen - } - case tp => tp + }) + case _: TypeOf => "" + case tp => toText(tp) } if (!suppressTypes) - txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close + txt = ("<" ~ txt ~ ":" ~ tpText ~ ">").close else if (tree.isType && !homogenizedView) - txt = toText(tp) + txt = tpText } if (!suppressPositions) { if (printPos) { From 116e9bdc114e726186cc75be890038818d8ee7f8 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 18:43:28 +0200 Subject: [PATCH 14/36] Also decompose proto for matches --- compiler/src/dotty/tools/dotc/core/Types.scala | 10 ++++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3576172b234f..a17ecd9044f8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3989,6 +3989,16 @@ object Types { case _ => None } } + + object Match { + def unapply(to: TypeOf): Option[(Type, List[Type])] = to.tree match { + case Trees.Match(cond, cases) => + // TODO: We only look at .body.tpe for now, eventually we should + // also take the guard and the pattern into account. + Some((cond.tpe, cases.map(_.body.tpe))) + case _ => None + } + } } // ----- TypeMaps -------------------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2cb59ad7b814..1c7d70407295 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -978,6 +978,11 @@ class Typer extends Namer val unchecked = pt.isRef(defn.PartialFunctionClass) typed(desugar.makeCaseLambda(tree.cases, protoFormals.length, unchecked) withPos tree.pos, pt) case _ => + val selectProto = pt match { + case TypeOf.Match(s, _) => s + case _ => WildcardType + } + val sel1 = typedExpr(tree.selector) val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen @@ -987,7 +992,7 @@ class Typer extends Namer } } - def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context) = { + def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context): List[CaseDef] = { /** gadtSyms = "all type parameters of enclosing methods that appear * non-variantly in the selector type" todo: should typevars @@ -1009,7 +1014,12 @@ class Typer extends Namer accu(Set.empty, selType) } - cases mapconserve (typedCase(_, pt, selType, gadtSyms)) + pt match { + case TypeOf.Match(_, cs) if cs.length == cases.length => + cases.zip(cs).mapconserve { case (c, p) => typedCase(c, p, selType, gadtSyms) } + case _ => + cases.mapconserve(typedCase(_, pt, selType, gadtSyms)) + } } /** Type a case. */ From 82c3deadf651f7bf3f6af27f55706301496b6a86 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 18:46:17 +0200 Subject: [PATCH 15/36] Add tests The commented parts are copy pasted from the AppliedTermRef PR --- tests/neg/transparent.scala | 31 +++++++++ tests/pos/transparent.scala | 121 ++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tests/neg/transparent.scala create mode 100644 tests/pos/transparent.scala diff --git a/tests/neg/transparent.scala b/tests/neg/transparent.scala new file mode 100644 index 000000000000..4b2bfb2c16be --- /dev/null +++ b/tests/neg/transparent.scala @@ -0,0 +1,31 @@ +object Invalid { + transparent def f(x: Int) = x + 1 + f(1): String // error + f(1): {0} // error +} + +// object SimpleEqs { +// val x = 1 +// val y: {x} = x +// implicitly[{x + 1} =:= {y}] // errror +// implicitly[{x + 1} =:= {y + 2}] // errror +// implicitly[{x + 1} =:= {1 + y}] // errror: TypeComparer doesn't know about commutativity + +// val b = true +// implicitly[{b} =:= {b}] +// implicitly[{!b} =:= {!b}] +// implicitly[{!b} =:= {b}] // errror +// } + + +// object Stability { +// def f1(x: Int): Int = x +// def f2(x: Int): {x} = x + +// val x = 1 +// implicitly[{f1(x)} =:= {x}] // errror: f1 is not considered stable // errror: f1's result type is not precise enough +// implicitly[{f1(x)} =:= {f1(x)}] // errror: f1 is not considered stable // errror: f1 is not considered stable +// implicitly[{f2(x)} =:= {x}] +// implicitly[{f2(x)} =:= {f2(x)}] +// implicitly[{f1(x)} =:= {f2(x)}] // errror: f1 is not considered stable // errror: f1's result type is not precise enough +// } diff --git a/tests/pos/transparent.scala b/tests/pos/transparent.scala new file mode 100644 index 000000000000..2ff724048e0f --- /dev/null +++ b/tests/pos/transparent.scala @@ -0,0 +1,121 @@ +object SimpleEqs { + val x = 1 + val y: {x} = x + // val z: {y + 1} = y + 1 + + type YPlusOne = {y + 1} +} + +object Call { + transparent def foo(x: Int) = 123 + foo(1): { foo(1) } + foo(1): Int +} + +object ITE { + transparent def foo1(b: Boolean) = { + val res = if (b) + 1 + else + 2 + identity[{ if (b) 1 else 2 }](res) + res + } + + transparent def foo2(b: Boolean): { if (b) 1 else 2 } = + if (b) 1 else 2 + + // Postponed until we can beta reduce + // foo(true): { if(true) 1 else 2 } + // foo(false): { if(false) 1 else 2 } + // var b: Boolean = true + // foo(b): { if(b) 1 else 2 } +} + +object Match { + transparent def foo1(b: Boolean) = { + val res = b match { + case true => 1 + case false => 2 + } + identity[{ b match { case true => 1; case false => 2 } }](res) + res + } + + transparent def foo(b: Boolean): { b match { case true => 1; case false => 2 } } = + b match { case true => 1; case false => 2 } +} + +// object AvoidLocalRefs { +// type Id[T] = T + +// val x = 1 +// def y = { val a: {x} = x; val t: Id[{a + 1}] = a + 1; t } +// def z: {x + 1} = { val a: {x} = x; val t: Id[{a + 1}] = a + 1; t } + +// { val a = 0; a + 1 } +// { val a = 0; 1 + a } +// } + + +// object Bounds { +// @annotation.implicitNotFound(msg = "Cannot prove that ${B} holds.") +// sealed abstract class P[B <: Boolean](val b: B) +// private[this] val prop_singleton = new P[true](true) {} +// object P { +// def assume(b: Boolean): P[b.type] = prop_singleton.asInstanceOf[P[b.type]] +// } + +// def if_(cond: Boolean): (implicit (ev: P[cond.type]) => Unit) => Unit = +// thn => if (cond) thn(P.assume(cond)) + + +// // Bounds-checked + +// def index(k: Int)(implicit ev: P[{k >= 0}]): Int = k + +// def run(i: Int) = +// if_(i >= 0) { +// index(i) +// } + + +// // Boxed value with a predicate + +// class PredBox[T, B <: Boolean](val v: T)(val p: P[B]) +// object PredBox { +// def apply[T, B <: Boolean](v: T)(implicit ev: P[B]) = new PredBox[T, B](v)(ev) +// } + +// def run2(i: Int) = +// if_(i != 0) { +// PredBox[Int, {i != 0}](i) +// } +// } + + +// object ArithmeticIdentities { +// type SInt = Int & Singleton + +// class DecomposeHelper[V <: SInt](val v: V) { +// import DecomposeHelper._ +// def asSumOf[X <: SInt, Y <: SInt](x: X, y: Y)(implicit ev: {v} =:= {x + y}): SumOf[{x}, {y}] = SumOf(x, y)(ev(v)) +// } + +// object DecomposeHelper { +// /* Axioms */ +// sealed trait Decomposition[V <: SInt] +// case class SumOf[X <: SInt, Y <: SInt](x: X, y: Y)(val v: {x + y}) extends Decomposition[{v}] { +// def commuted: SumOf[Y, X] = SumOf(y, x)(v.asInstanceOf[{y + x}]) +// } +// } + +// implicit def toDecomposeHelper[V <: Int](v: V): DecomposeHelper[v.type] = new DecomposeHelper(v) + + +// // Let's "show" that x + 1 == 1 + x + +// val x = 123 +// (x + 1).asSumOf(x, 1).v: {x + 1} +// (x + 1).asSumOf(x, 1).commuted.v: {1 + x} +// } From f3c9235b7b75f559b6e7513b47972cbd29fabf49 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Wed, 13 Jun 2018 18:51:14 +0200 Subject: [PATCH 16/36] Implement `derivedTypeOf` in `ApproximatingTypeMap` --- compiler/src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index a17ecd9044f8..7c14c7962e1c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4426,6 +4426,16 @@ object Types { if (underlying.isBottomType) underlying else tp.derivedAnnotatedType(underlying, annot) } + + override protected def derivedTypeOf(tp: TypeOf, underlying: Type, tree: Tree) = + underlying match { + case Range(lo, hi) => + range(tp.derivedTypeOf(lo, tree), tp.derivedTypeOf(hi, tree)) + case _ => + if (underlying.isBottomType) underlying + else tp.derivedTypeOf(underlying, tree) + } + override protected def derivedWildcardType(tp: WildcardType, bounds: Type) = { tp.derivedWildcardType(rangeToBounds(bounds)) } @@ -4445,10 +4455,6 @@ object Types { tp.derivedLambdaType(tp.paramNames, formals, restpe) } - // TODO: Implement derivedTypeOf in ApproximatingTypeMap -// override protected def derivedTypeOf(tp: TypeOf, tree: Tree, underlyingTp: Type): Type = -// tp.derivedTypeOf(tree, underlyingTp) - protected def reapply(tp: Type): Type = apply(tp) } From 2d4a9264166f9420a8c3fa40c229e43c57ed9ec2 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 19:47:09 +0200 Subject: [PATCH 17/36] fixup! Avoid infinite loop when printing TypeOf --- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 520e1d06abb2..3b1e3fb19e4d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -38,6 +38,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ private[this] var enclosingDef: untpd.Tree = untpd.EmptyTree private[this] var myCtx: Context = super.ctx + private[this] var printTypeOfTree: Boolean = true private[this] var printPos = ctx.settings.YprintPos.value private[this] val printLines = ctx.settings.printLines.value @@ -552,7 +553,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ExprType(tp.widen) case _ => tp.widen }) - case _: TypeOf => "" + case TypeOf(tpe, tree) => + if (printTypeOfTree) + try { + // Only print 1 level of TypeOf; breaks the type→tree→type loop + printTypeOfTree = false + toText(tree) + } finally { + printTypeOfTree = true + } + else toText(tpe) case tp => toText(tp) } if (!suppressTypes) From 6289f21823a3cc739f451412325a202f00d08f38 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 19:48:28 +0200 Subject: [PATCH 18/36] TypeOf shouldn't be handled by AnnotatedType logic AnnotatedType uses precise equivalance of Trees, whereas TypeOf is only concerned with certain top level trees. --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 0a91a8e36b26..a961ce6c994d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -569,7 +569,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } compareTypeBounds - case tp2: AnnotatedType if tp2.isRefining => + case tp2: AnnotatedType if tp2.isRefining && !tp2.isInstanceOf[TypeOf] => (tp1.derivesAnnotWith(tp2.annot.sameAnnotation) || defn.isBottomType(tp1)) && recur(tp1, tp2.parent) case ClassInfo(pre2, cls2, _, _, _) => From 93fa77a017c4e4fe5f1c903a784aaad01fc408d6 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 20:03:24 +0200 Subject: [PATCH 19/36] fixup! Avoid infinite loop when printing TypeOf --- compiler/src/dotty/tools/dotc/core/Types.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7c14c7962e1c..423ffc00ccaf 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3970,6 +3970,7 @@ object Types { object TypeOf { def apply(underlyingTp: Type, tree: Tree)(implicit ctx: Context): TypeOf = new TypeOf(underlyingTp, tree) + def unapply(to: TypeOf): Option[(Type, Tree)] = Some((to.underlyingTp, to.tree)) /** To be used from type assigner. The assumption is that tree is currently * being type assigned, and will be typed by the time it reaches the From 32d9858b0bcd6f62dc828599bfe63b8a89d4ccae Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 13 Jun 2018 20:05:17 +0200 Subject: [PATCH 20/36] Update TypeMap to use assignTypes Also, restore the invariant that the type of the TypeOf tree is the TypeOf type itself. Here is an example showing what would go wrong if we didn't do that. Suppose we have def f(x: Int) = x def g(x: Int) = 2 * x def h(f: Int => Int) = f(1) h(g): { 2 * 1 } Given a type map substituting f for g in f(1), the underlying type should be substituted to the result type of g(1), that is, 2 * 1. --- .../src/dotty/tools/dotc/core/Types.scala | 34 +++++++++++++++---- tests/pos/transparent.scala | 6 ++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 423ffc00ccaf..38e29a7b7bba 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3932,8 +3932,7 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- class TypeOf(val underlyingTp: Type, val tree: Tree)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { - assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree: $tree") - assert(!underlyingTp.isInstanceOf[TypeOf]) + assert(TypeOf.isLegalTopLevelTree(tree), i"Illegal top-level tree in TypeOf: $tree") override def equals(that: Any): Boolean = { that match { @@ -4149,19 +4148,40 @@ object Types { val tp1 = this(tree.tpe) if (tp eq tp1) tree else tree.withTypeUnchecked(tp1).asInstanceOf[ThisTree] } + val ta = ctx.typeAssigner + import ta.assignType val tree1 = tp.tree match { case tree: TypeApply => - cpy.TypeApply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) + val fun = copyMapped(tree.fun) + val args = tree.args.mapConserve(copyMapped) + assignType(cpy.TypeApply(tree)(fun, args), fun, args) case tree: Apply => - cpy.Apply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) + val fun = copyMapped(tree.fun) + val args = tree.args.mapConserve(copyMapped) + assignType(cpy.Apply(tree)(fun, args), fun, args) case tree: If => - cpy.If(tree)(copyMapped(tree.cond), copyMapped(tree.thenp), copyMapped(tree.elsep)) + val thenp = copyMapped(tree.thenp) + val elsep = copyMapped(tree.elsep) + assignType(cpy.If(tree)(copyMapped(tree.cond), thenp, elsep), thenp, elsep) case tree: Match => - cpy.Match(tree)(copyMapped(tree.selector), tree.cases.mapConserve(copyMapped)) + val cases = tree.cases.mapConserve(copyMapped) + assignType(cpy.Match(tree)(copyMapped(tree.selector), cases), cases) case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } - derivedTypeOf(tp, this(tp.underlyingTp), tree1) + val result = derivedTypeOf(tp, tree1.tpe, tree1) + // Restore the invariant that the type of the TypeOf tree is the + // TypeOf type itself. Here is an example showing what would go wrong + // if we didn't do that. Suppose we have + // def f(x: Int) = x + // def g(x: Int) = 2 * x + // def h(f: Int => Int) = f(1) + // h(g): { 2 * 1 } + // Given a type map substituting f for g in f(1), the underlying + // type should be substituted to the result type of g(1), that is, + // 2 * 1. + tree1.overwriteType(result) + result case tp @ AnnotatedType(underlying, annot) => val underlying1 = this(underlying) diff --git a/tests/pos/transparent.scala b/tests/pos/transparent.scala index 2ff724048e0f..10b3915a33ff 100644 --- a/tests/pos/transparent.scala +++ b/tests/pos/transparent.scala @@ -46,6 +46,12 @@ object Match { b match { case true => 1; case false => 2 } } +object Applied { + transparent def foo1(b: Boolean) = ??? + transparent def foo2(b: Boolean): { foo1(b) } = foo1(b) + val a: { foo2(true) } = foo2(true) +} + // object AvoidLocalRefs { // type Id[T] = T From 3f9d6e60e14eeda94bb8080087eec35c40bb1ba1 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 10:38:23 +0200 Subject: [PATCH 21/36] Fix `TypeMap` and implement `TypeAccumulator` for `TypeOf` --- .../src/dotty/tools/dotc/core/Types.scala | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 38e29a7b7bba..e8fe768ef14d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4148,40 +4148,36 @@ object Types { val tp1 = this(tree.tpe) if (tp eq tp1) tree else tree.withTypeUnchecked(tp1).asInstanceOf[ThisTree] } - val ta = ctx.typeAssigner - import ta.assignType val tree1 = tp.tree match { case tree: TypeApply => - val fun = copyMapped(tree.fun) - val args = tree.args.mapConserve(copyMapped) - assignType(cpy.TypeApply(tree)(fun, args), fun, args) + cpy.TypeApply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) case tree: Apply => - val fun = copyMapped(tree.fun) - val args = tree.args.mapConserve(copyMapped) - assignType(cpy.Apply(tree)(fun, args), fun, args) + cpy.Apply(tree)(copyMapped(tree.fun), tree.args.mapConserve(copyMapped)) case tree: If => - val thenp = copyMapped(tree.thenp) - val elsep = copyMapped(tree.elsep) - assignType(cpy.If(tree)(copyMapped(tree.cond), thenp, elsep), thenp, elsep) + cpy.If(tree)(copyMapped(tree.cond), copyMapped(tree.thenp), copyMapped(tree.elsep)) case tree: Match => - val cases = tree.cases.mapConserve(copyMapped) - assignType(cpy.Match(tree)(copyMapped(tree.selector), cases), cases) + cpy.Match(tree)(copyMapped(tree.selector), tree.cases.mapConserve(copyMapped)) case tree => throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } - val result = derivedTypeOf(tp, tree1.tpe, tree1) - // Restore the invariant that the type of the TypeOf tree is the - // TypeOf type itself. Here is an example showing what would go wrong - // if we didn't do that. Suppose we have - // def f(x: Int) = x - // def g(x: Int) = 2 * x - // def h(f: Int => Int) = f(1) - // h(g): { 2 * 1 } - // Given a type map substituting f for g in f(1), the underlying - // type should be substituted to the result type of g(1), that is, - // 2 * 1. - tree1.overwriteType(result) - result + if (tp.tree ne tree1) { + val result = derivedTypeOf(tp, tree1.tpe, tree1) + assert(tp ne result) + // Restore the invariant that the type of the TypeOf tree is the + // TypeOf type itself. Here is an example showing what would go wrong + // if we didn't do that. Suppose we have + // def f(x: Int) = x + // def g(x: Int) = 2 * x + // def h(f: Int => Int) = f(1) + // h(g): { 2 * 1 } + // Given a type map substituting f for g in f(1), the underlying + // type should be substituted to the result type of g(1), that is, + // 2 * 1. + tree1.overwriteType(result) + result + } else { + tp + } case tp @ AnnotatedType(underlying, annot) => val underlying1 = this(underlying) @@ -4564,6 +4560,25 @@ object Types { case tp: OrType => this(this(x, tp.tp1), tp.tp2) + case tp: TypeOf => + @tailrec def foldTrees(x: T, ts: List[Tree]): T = ts match { + case t :: ts1 => foldTrees(apply(x, t.tpe), ts1) + case nil => x + } + + tp.tree match { + case tree: TypeApply => + foldTrees(this(x, tree.fun.tpe), tree.args) + case tree: Apply => + foldTrees(this(x, tree.fun.tpe), tree.args) + case tree: If => + this(this(this(x, tree.cond.tpe), tree.thenp.tpe), tree.elsep.tpe) + case tree: Match => + foldTrees(this(x, tree.selector.tpe), tree.cases) + case tree => + throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") + } + case AnnotatedType(underlying, annot) => this(applyToAnnot(x, annot), underlying) From fed6013ab829a7745749c1e86783c300a0679578 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 10:39:24 +0200 Subject: [PATCH 22/36] Fix pretty printing of `TypeOf` We now effectively -Xprint-types for trees within TypeOf. Instead, we show the underlying type for the top-level tree. Also added toString in TypeOf. --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 ++ .../tools/dotc/printing/PlainPrinter.scala | 18 ++++++++++++++---- .../tools/dotc/printing/RefinedPrinter.scala | 13 ++----------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index e8fe768ef14d..d81ab936cc33 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3965,6 +3965,8 @@ object Types { override def derivedAnnotatedType(parent: Type, annot: Annotation): AnnotatedType = if ((parent eq this.parent) && (annot eq this.annot)) this else TypeOf(parent, annot.arguments.head) + + override def toString(): String = s"TypeOf($underlyingTp, $tree)" } object TypeOf { diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 15f0219e0e05..54a5f3ec6247 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -18,6 +18,13 @@ class PlainPrinter(_ctx: Context) extends Printer { protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) private[this] var openRecs: List[RecType] = Nil + protected[this] var isInTypeOf: Boolean = true + + protected final def inTypeOf(op: => Text): Text = { + val saved = isInTypeOf + isInTypeOf = false + try { op } finally { isInTypeOf = saved } + } protected def maxToTextRecursions = 100 @@ -183,8 +190,11 @@ class PlainPrinter(_ctx: Context) extends Printer { (Str(" => ") provided !tp.resultType.isInstanceOf[MethodType]) ~ toTextGlobal(tp.resultType) } - case tp: TypeOf => - "{ " ~ toTextLocal(tp.tree) ~ " }" + case tp @ TypeOf(underlyingTp, tree) => + val underlying: Text = + if (ctx.settings.XprintTypes.value) " <: " ~ toText(underlyingTp) + else Text() + "{ " ~ inTypeOf { toTextLocal(tree) } ~ underlying ~ " }" case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) case tp: TypeVar => @@ -486,9 +496,9 @@ class PlainPrinter(_ctx: Context) extends Printer { val elems = Text(tree.productIterator.map(toTextElem).toList, ", ") val tpSuffix: Text = - if (ctx.settings.XprintTypes.value && tree.hasType) + if (ctx.settings.XprintTypes.value && tree.hasType && !isInTypeOf) tree.typeOpt match { - case tp: TypeOf if tp.tree.tpe eq tp => " | " + case tp: TypeOf if tp.tree eq tree => " | " case tp => " | " ~ toText(tree.typeOpt) } else diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3b1e3fb19e4d..c9d23de54d9d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -527,7 +527,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { var txt = toTextCore(tree) def suppressTypes = - tree.isType || tree.isDef || // don't print types of types or defs + tree.isType || isInTypeOf || tree.isDef || // don't print types of types or defs homogenizedView && ctx.mode.is(Mode.Pattern) // When comparing pickled info, disregard types of patterns. // The reason is that GADT matching can rewrite types of pattern trees @@ -553,16 +553,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ExprType(tp.widen) case _ => tp.widen }) - case TypeOf(tpe, tree) => - if (printTypeOfTree) - try { - // Only print 1 level of TypeOf; breaks the type→tree→type loop - printTypeOfTree = false - toText(tree) - } finally { - printTypeOfTree = true - } - else toText(tpe) + case tp: TypeOf if tp.tree eq tree => "" case tp => toText(tp) } if (!suppressTypes) From 367a44a677a33a90331bfabff12de3e27bd4c955 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 13:57:56 +0200 Subject: [PATCH 23/36] Fix `isInTypeOf` state used for pretty printing `TypeOf`s --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 4 ++-- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 54a5f3ec6247..baa0ce76339a 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -18,11 +18,11 @@ class PlainPrinter(_ctx: Context) extends Printer { protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) private[this] var openRecs: List[RecType] = Nil - protected[this] var isInTypeOf: Boolean = true + protected[this] var isInTypeOf: Boolean = false protected final def inTypeOf(op: => Text): Text = { val saved = isInTypeOf - isInTypeOf = false + isInTypeOf = true try { op } finally { isInTypeOf = saved } } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c9d23de54d9d..183d41ccdd97 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -38,7 +38,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ private[this] var enclosingDef: untpd.Tree = untpd.EmptyTree private[this] var myCtx: Context = super.ctx - private[this] var printTypeOfTree: Boolean = true private[this] var printPos = ctx.settings.YprintPos.value private[this] val printLines = ctx.settings.printLines.value From a39574236f3b377af297b2b2716177d7e812b7fd Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 14:13:40 +0200 Subject: [PATCH 24/36] Add orphan check for underlying types of `TypeOf`s --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index e1c36b9395c3..3818d5a759c9 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -499,6 +499,9 @@ object TreeChecker { case tp: TypeVar => assert(tp.isInstantiated, s"Uninstantiated type variable: ${tp.show}, tree = ${tree.show}") apply(tp.underlying) + case tp: TypeOf => + apply(tp.underlyingTp) + mapOver(tp) case _ => mapOver(tp) } From 84cc5bfdb43d1017cc6e3b954a1cc0e73f5f75c3 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 15:17:02 +0200 Subject: [PATCH 25/36] Fix pretty printing of `TypeOf`s to reveal types, not trees (which might be out of date) --- .../tools/dotc/printing/PlainPrinter.scala | 12 ++++----- .../tools/dotc/printing/RefinedPrinter.scala | 27 ++++++++++++++++--- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index baa0ce76339a..b2da7142ae1c 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -149,7 +149,10 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: TypeParamRef => ParamRefNameString(tp) ~ lambdaHash(tp.binder) case tp: SingletonType => - toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" + if (isInTypeOf) + toTextRef(tp) + else + toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")" case AppliedType(tycon, args) => (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close case tp: RefinedType => @@ -190,11 +193,8 @@ class PlainPrinter(_ctx: Context) extends Printer { (Str(" => ") provided !tp.resultType.isInstanceOf[MethodType]) ~ toTextGlobal(tp.resultType) } - case tp @ TypeOf(underlyingTp, tree) => - val underlying: Text = - if (ctx.settings.XprintTypes.value) " <: " ~ toText(underlyingTp) - else Text() - "{ " ~ inTypeOf { toTextLocal(tree) } ~ underlying ~ " }" + case TypeOf(underlyingTp, _) => + "{ ... <: " ~ toText(underlyingTp) ~ " }" case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) case tp: TypeVar => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 183d41ccdd97..3b5e7e6d722d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -196,6 +196,27 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return toTextParents(tp.parents) ~ "{...}" case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" + case TypeOf(underlyingTp, tree) => + import tpd._ + val underlying: Text = " <: " ~ toText(underlyingTp) provided ctx.settings.XprintTypes.value + def treeText = tree match { + case TypeApply(fun, args) => toTextLocal(fun.tpe) ~ "[" ~ toTextGlobal(args.tpes, ", ") ~ "]" + case Apply(fun, args) => toTextLocal(fun.tpe) ~ "(" ~ toTextGlobal(args.tpes, ", ") ~ ")" + case If(cond, thenp, elsep) => + changePrec(GlobalPrec) { + keywordStr("if ") ~ toText(cond.tpe) ~ keywordText(" then ") ~ toText(thenp.tpe) ~ + (keywordStr(" else ") ~ toText(elsep.tpe) provided !elsep.isEmpty) + } + case Match(sel, cases) => + val blockTexts = cases map { case CaseDef(pat, guard, body) => + keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ + toText(List(body.tpe), "\n") + } + val blockText = ("{" ~ Text(blockTexts, "\n") ~ "}").close + if (sel.isEmpty) blockText + else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText } + } + return typeText("{ ") ~ inTypeOf { treeText } ~ underlying ~ typeText(" }") case tp: AnnotatedType if homogenizedView => // Positions of annotations in types are not serialized // (they don't need to because we keep the original type tree with @@ -372,7 +393,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case TypeTree() => typeText(toText(tree.typeOpt)) case SingletonTypeTree(ref) => - "{ " ~ toTextLocal(ref) ~ " }" + typeText("{") ~~ toTextLocal(ref) ~~ typeText("}") case AndTypeTree(l, r) => changePrec(AndPrec) { toText(l) ~ " & " ~ toText(r) } case OrTypeTree(l, r) => @@ -727,8 +748,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def optText[T >: Untyped](tree: Tree[T])(encl: Text => Text): Text = if (tree.isEmpty) "" else encl(toText(tree)) - def optText[T >: Untyped](tree: List[Tree[T]])(encl: Text => Text): Text = - if (tree.exists(!_.isEmpty)) encl(blockText(tree)) else "" + def optText[T >: Untyped](trees: List[Tree[T]])(encl: Text => Text): Text = + if (trees.exists(!_.isEmpty)) encl(blockText(trees)) else "" override protected def ParamRefNameString(name: Name): String = name.invariantName.toString From 705f864aa31f8977b07556a0c6daf9427f364900 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 16:26:39 +0200 Subject: [PATCH 26/36] WIP Fixing duplicate TypeOf --- .../dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 15 +++++++++------ .../dotty/tools/dotc/transform/TreeChecker.scala | 7 ++++--- .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/pos/transparent.scala | 11 ++++++----- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a5c26588f819..5017508f8eaa 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -787,7 +787,7 @@ object SymDenotations { /** Does this symbol have a transparent owner, itself included? */ def isTransitivelyTransparent(implicit ctx: Context): Boolean = - ownersIterator.exists(_.isTransparentMethod) + ownersIterator.exists(_.isTransparentMethod) || ctx.outersIterator.exists(_.tree.isInstanceOf[SingletonTypeTree[_]]) def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d81ab936cc33..ebac8962a060 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -87,12 +87,12 @@ object Types { // ----- Tests ----------------------------------------------------- // // debug only: a unique identifier for a type -// val uniqId = { -// nextId = nextId + 1 + val uniqId = { + nextId = nextId + 1 // if (nextId == 19555) // println("foo") -// nextId -// } + nextId + } /** A cache indicating whether the type was still provisional, last time we checked */ @sharable private var mightBeProvisional = true @@ -3931,8 +3931,11 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- + // TODO: Move Annotation construction to TypeOf.apply(...) class TypeOf(val underlyingTp: Type, val tree: Tree)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { assert(TypeOf.isLegalTopLevelTree(tree), i"Illegal top-level tree in TypeOf: $tree") + if (uniqId == 5705) + new Throwable().printStackTrace() override def equals(that: Any): Boolean = { that match { @@ -3953,7 +3956,7 @@ object Types { false } } - compareTree(this.tree, that.tree) && { assert(this.underlyingTp == that.underlyingTp); true } + compareTree(this.tree, that.tree) && { assert(this.underlyingTp == that.underlyingTp, s"UTP:\n\t${this.underlyingTp}\n\t${that.underlyingTp}"); true } case _ => false } } @@ -3966,7 +3969,7 @@ object Types { if ((parent eq this.parent) && (annot eq this.annot)) this else TypeOf(parent, annot.arguments.head) - override def toString(): String = s"TypeOf($underlyingTp, $tree)" + override def toString(): String = s"TypeOf($underlyingTp, $tree)#$uniqId" } object TypeOf { diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 3818d5a759c9..396353c1cfad 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -486,7 +486,7 @@ object TreeChecker { /** - Check that TypeParamRefs and MethodParams refer to an enclosing type. * - Check that all type variables are instantiated. */ - def checkNoOrphans(tp0: Type, tree: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = new TypeMap() { + def checkNoOrphans(tp0: Type)(implicit ctx: Context) = new TypeMap() { val definedBinders = new java.util.IdentityHashMap[Type, Any] def apply(tp: Type): Type = { tp match { @@ -495,11 +495,12 @@ object TreeChecker { mapOver(tp) definedBinders.remove(tp) case tp: ParamRef => - assert(definedBinders.get(tp.binder) != null, s"orphan param: ${tp.show}, hash of binder = ${System.identityHashCode(tp.binder)}, tree = ${tree.show}, type = $tp0") + assert(definedBinders.get(tp.binder) != null, s"orphan param: ${tp.show}, hash of binder = ${System.identityHashCode(tp.binder)}, type = $tp0") case tp: TypeVar => - assert(tp.isInstantiated, s"Uninstantiated type variable: ${tp.show}, tree = ${tree.show}") + assert(tp.isInstantiated, s"Uninstantiated type variable: ${tp.show}") apply(tp.underlying) case tp: TypeOf => + assert(tp.tree.tpe eq tp, s"A TypeOf's tree's type should point back to the TypeOf: $tp\n\tshow: ${tp.show}") apply(tp.underlyingTp) mapOver(tp) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 8e089458d8c9..033ce3092a11 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -503,8 +503,10 @@ trait TypeAssigner { case _: Literal | _: Ident | _: Select | _: Block => ref.tpe case _ => if (TypeOf.isLegalTopLevelTree(ref)) - if (ref.tpe.isInstanceOf[TypeOf]) ref.tpe - else TypeOf(ref.tpe, ref) + if (ref.tpe.isInstanceOf[TypeOf]) + ref.tpe + else + errorType(i"Non-sensical singleton-type expression: $ref", ref.pos) else throw new AssertionError(i"Tree $ref is not a valid reference for a singleton type tree.") } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1c7d70407295..d1c1971a557a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1186,7 +1186,7 @@ class Typer extends Namer } def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { - val ref1 = typedExpr(tree.ref) + val ref1 = typedExpr(tree.ref)(ctx.fresh.setTree(tree)) // TODO: Discuss stability requirements of singleton type trees and potentially reenable check // checkStable(ref1.tpe, tree.pos) assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) diff --git a/tests/pos/transparent.scala b/tests/pos/transparent.scala index 10b3915a33ff..b2d9f83b3397 100644 --- a/tests/pos/transparent.scala +++ b/tests/pos/transparent.scala @@ -1,9 +1,9 @@ -object SimpleEqs { +/*object SimpleEqs { val x = 1 val y: {x} = x // val z: {y + 1} = y + 1 - type YPlusOne = {y + 1} + // type YPlusOne = {y + 1} } object Call { @@ -44,12 +44,13 @@ object Match { transparent def foo(b: Boolean): { b match { case true => 1; case false => 2 } } = b match { case true => 1; case false => 2 } -} +}*/ object Applied { transparent def foo1(b: Boolean) = ??? - transparent def foo2(b: Boolean): { foo1(b) } = foo1(b) - val a: { foo2(true) } = foo2(true) + // transparent def foo2(b: Boolean): { foo1(b) } = foo1(b) + transparent def foo2(b: Boolean) = foo1(b) + // val a: { foo2(true) } = foo2(true) } // object AvoidLocalRefs { From 169fa7480c37e2b1207636b018cdb38091148fd8 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Thu, 14 Jun 2018 17:30:06 +0200 Subject: [PATCH 27/36] Change trees in TypeOf to never loop back to their TypeOf In the new scheme, we never touch a TypeOf tree's type and ensure this by assigning that tree NoType. This currently induces additional tree copies which could be removed at a later point. In the process we also fixed bugs in TreeCopier, which would sometimes not go through TypeAssigner when it should have. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- .../src/dotty/tools/dotc/core/Types.scala | 26 ++++++++++--------- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 2 +- tests/neg/transparent.scala | 3 +++ tests/pos/transparent.scala | 11 +++----- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a9feeae2da99..324af8d4b711 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -540,7 +540,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = { val tree1 = untpd.cpy.If(tree)(cond, thenp, elsep) tree match { - case tree: If if (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe) + case tree: If if (cond.tpe eq tree.cond.tpe) && (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe) case _ => ta.assignType(tree1, thenp, elsep) } } @@ -557,7 +557,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = { val tree1 = untpd.cpy.Match(tree)(selector, cases) tree match { - case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) + case tree: Match if (selector.tpe eq tree.selector.tpe) && sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) case _ => ta.assignType(tree1, cases) } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ebac8962a060..7eaed265515f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3934,8 +3934,6 @@ object Types { // TODO: Move Annotation construction to TypeOf.apply(...) class TypeOf(val underlyingTp: Type, val tree: Tree)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { assert(TypeOf.isLegalTopLevelTree(tree), i"Illegal top-level tree in TypeOf: $tree") - if (uniqId == 5705) - new Throwable().printStackTrace() override def equals(that: Any): Boolean = { that match { @@ -3973,7 +3971,14 @@ object Types { } object TypeOf { - def apply(underlyingTp: Type, tree: Tree)(implicit ctx: Context): TypeOf = new TypeOf(underlyingTp, tree) + def apply(underlyingTp: Type, tree: untpd.Tree)(implicit ctx: Context): TypeOf = { + val tree1 = tree.clone.asInstanceOf[Tree] + // TODO: This is a safety net to keep us from touching a TypeOf's tree's type. + // Assuming we never look at this type, it would be safe to simply reuse tree without cloning. + // The invariant is currently enforced by TreeChecker. + tree1.overwriteType(NoType) + new TypeOf(underlyingTp, tree1) + } def unapply(to: TypeOf): Option[(Type, Tree)] = Some((to.underlyingTp, to.tree)) /** To be used from type assigner. The assumption is that tree is currently @@ -3981,7 +3986,7 @@ object Types { * outside world. */ private[dotc] def fromUntyped(tpe: Type, tree: untpd.Tree)(implicit ctx: Context): TypeOf = - TypeOf(tpe, tree.asInstanceOf[Tree]) + TypeOf(tpe, tree) private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { case _: TypeApply | _: Apply | _: If | _: Match => true @@ -4151,7 +4156,7 @@ object Types { case tp: TypeOf => def copyMapped[ThisTree <: Tree](tree: ThisTree): ThisTree = { val tp1 = this(tree.tpe) - if (tp eq tp1) tree else tree.withTypeUnchecked(tp1).asInstanceOf[ThisTree] + if (tree.tpe eq tp1) tree else tree.withTypeUnchecked(tp1).asInstanceOf[ThisTree] } val tree1 = tp.tree match { case tree: TypeApply => @@ -4166,11 +4171,8 @@ object Types { throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } if (tp.tree ne tree1) { - val result = derivedTypeOf(tp, tree1.tpe, tree1) - assert(tp ne result) - // Restore the invariant that the type of the TypeOf tree is the - // TypeOf type itself. Here is an example showing what would go wrong - // if we didn't do that. Suppose we have + // Here is an example showing what would go wrong if we didn't re-assign types: + // Suppose we have // def f(x: Int) = x // def g(x: Int) = 2 * x // def h(f: Int => Int) = f(1) @@ -4178,8 +4180,8 @@ object Types { // Given a type map substituting f for g in f(1), the underlying // type should be substituted to the result type of g(1), that is, // 2 * 1. - tree1.overwriteType(result) - result + assert(!tp.underlyingTp.exists || tree1.tpe.exists, i"Derived TypeOf's type became NoType") + tree1.tpe } else { tp } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index b2da7142ae1c..a711823f375b 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -498,7 +498,7 @@ class PlainPrinter(_ctx: Context) extends Printer { val tpSuffix: Text = if (ctx.settings.XprintTypes.value && tree.hasType && !isInTypeOf) tree.typeOpt match { - case tp: TypeOf if tp.tree eq tree => " | " + case tp: TypeOf => " | " case tp => " | " ~ toText(tree.typeOpt) } else diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3b5e7e6d722d..c384f4068b3a 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -573,7 +573,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ExprType(tp.widen) case _ => tp.widen }) - case tp: TypeOf if tp.tree eq tree => "" + case tp: TypeOf => "" case tp => toText(tp) } if (!suppressTypes) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 396353c1cfad..236fceebbd5b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -500,7 +500,7 @@ object TreeChecker { assert(tp.isInstantiated, s"Uninstantiated type variable: ${tp.show}") apply(tp.underlying) case tp: TypeOf => - assert(tp.tree.tpe eq tp, s"A TypeOf's tree's type should point back to the TypeOf: $tp\n\tshow: ${tp.show}") + assert(tp.tree.tpe eq NoType, s"A TypeOf's tree's type should point back to the TypeOf: $tp") apply(tp.underlyingTp) mapOver(tp) case _ => diff --git a/tests/neg/transparent.scala b/tests/neg/transparent.scala index 4b2bfb2c16be..67a26b6f4f56 100644 --- a/tests/neg/transparent.scala +++ b/tests/neg/transparent.scala @@ -2,6 +2,9 @@ object Invalid { transparent def f(x: Int) = x + 1 f(1): String // error f(1): {0} // error + + val y: Int = ??? + type YPlusOne = {y + 1} // error } // object SimpleEqs { diff --git a/tests/pos/transparent.scala b/tests/pos/transparent.scala index b2d9f83b3397..1e15a11f598e 100644 --- a/tests/pos/transparent.scala +++ b/tests/pos/transparent.scala @@ -1,9 +1,7 @@ -/*object SimpleEqs { +object SimpleEqs { val x = 1 val y: {x} = x // val z: {y + 1} = y + 1 - - // type YPlusOne = {y + 1} } object Call { @@ -44,13 +42,12 @@ object Match { transparent def foo(b: Boolean): { b match { case true => 1; case false => 2 } } = b match { case true => 1; case false => 2 } -}*/ +} object Applied { transparent def foo1(b: Boolean) = ??? - // transparent def foo2(b: Boolean): { foo1(b) } = foo1(b) - transparent def foo2(b: Boolean) = foo1(b) - // val a: { foo2(true) } = foo2(true) + transparent def foo2(b: Boolean): { foo1(b) } = foo1(b) + val a: { foo2(true) } = foo2(true) } // object AvoidLocalRefs { From 43d4d747650ecff8fc4a1a14e7d3a7ad3d671222 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 10:19:05 +0200 Subject: [PATCH 28/36] Remove TypeOf.fromUntyped --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ------- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 8 ++++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7eaed265515f..0e24273d0f49 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3981,13 +3981,6 @@ object Types { } def unapply(to: TypeOf): Option[(Type, Tree)] = Some((to.underlyingTp, to.tree)) - /** To be used from type assigner. The assumption is that tree is currently - * being type assigned, and will be typed by the time it reaches the - * outside world. - */ - private[dotc] def fromUntyped(tpe: Type, tree: untpd.Tree)(implicit ctx: Context): TypeOf = - TypeOf(tpe, tree) - private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { case _: TypeApply | _: Apply | _: If | _: Match => true case _ => false diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 033ce3092a11..8c1e44a09b9c 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -368,7 +368,7 @@ trait TypeAssigner { val tpe = if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes) else fntpe.resultType - if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tpe, tree) + if (fn.symbol.isTransparentMethod) TypeOf(tpe, tree) else tpe } else errorType(i"wrong number of arguments at ${ctx.phase.prev} for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.pos) @@ -429,7 +429,7 @@ trait TypeAssigner { val argTypes = args.tpes if (sameLength(argTypes, paramNames)) { val tpe = pt.instantiate(argTypes) - if (fn.symbol.isTransparentMethod) TypeOf.fromUntyped(tpe, tree) + if (fn.symbol.isTransparentMethod) TypeOf(tpe, tree) else tpe } else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) @@ -462,7 +462,7 @@ trait TypeAssigner { def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = { val underlying = thenp.tpe | elsep.tpe if (ctx.owner.isTransitivelyTransparent) - tree.withType(TypeOf.fromUntyped(underlying, tree)) + tree.withType(TypeOf(underlying, tree)) else tree.withType(underlying) } @@ -478,7 +478,7 @@ trait TypeAssigner { def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = { val underlying = ctx.typeComparer.lub(cases.tpes) if (ctx.owner.isTransitivelyTransparent) - tree.withType(TypeOf.fromUntyped(underlying, tree)) + tree.withType(TypeOf(underlying, tree)) else tree.withType(underlying) } From a2ebbc41c6235d9bf06960bcfebecd641ed6a098 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 10:31:55 +0200 Subject: [PATCH 29/36] Create TypeOfAnnot in .pply to prevent ctx capture --- .../src/dotty/tools/dotc/core/Types.scala | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0e24273d0f49..62a7c1661cb6 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3707,7 +3707,7 @@ object Types { override def underlying(implicit ctx: Context): Type = parent - def derivedAnnotatedType(parent: Type, annot: Annotation) = + def derivedAnnotatedType(parent: Type, annot: Annotation)(implicit ctx: Context) = if ((parent eq this.parent) && (annot eq this.annot)) this else AnnotatedType(parent, annot) @@ -3931,9 +3931,9 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- - // TODO: Move Annotation construction to TypeOf.apply(...) - class TypeOf(val underlyingTp: Type, val tree: Tree)(implicit ctx: Context) extends AnnotatedType(underlyingTp, Annotation(defn.TypeOfAnnot, tree)) { - assert(TypeOf.isLegalTopLevelTree(tree), i"Illegal top-level tree in TypeOf: $tree") + /** */ + class TypeOf(val underlyingTp: Type, val tree: Tree, annot: Annotation) extends AnnotatedType(underlyingTp, annot) { + assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree in TypeOf: $tree") override def equals(that: Any): Boolean = { that match { @@ -3963,7 +3963,7 @@ object Types { if ((this.tree eq tree) && (this.underlyingTp eq underlyingTp)) this else TypeOf(underlyingTp, tree) - override def derivedAnnotatedType(parent: Type, annot: Annotation): AnnotatedType = + override def derivedAnnotatedType(parent: Type, annot: Annotation)(implicit ctx: Context): AnnotatedType = if ((parent eq this.parent) && (annot eq this.annot)) this else TypeOf(parent, annot.arguments.head) @@ -3973,15 +3973,17 @@ object Types { object TypeOf { def apply(underlyingTp: Type, tree: untpd.Tree)(implicit ctx: Context): TypeOf = { val tree1 = tree.clone.asInstanceOf[Tree] - // TODO: This is a safety net to keep us from touching a TypeOf's tree's type. - // Assuming we never look at this type, it would be safe to simply reuse tree without cloning. - // The invariant is currently enforced by TreeChecker. + // This is a safety net to keep us from touching a TypeOf's tree's type. + // Assuming we never look at this type, it would be safe to simply reuse + // tree without cloning. The invariant is currently enforced in Ycheck. + // To disable this safety net we will also have to update the pickler + // to ignore the type of the TypeOf tree's. tree1.overwriteType(NoType) - new TypeOf(underlyingTp, tree1) + new TypeOf(underlyingTp, tree1, Annotation(defn.TypeOfAnnot, tree1)) } def unapply(to: TypeOf): Option[(Type, Tree)] = Some((to.underlyingTp, to.tree)) - private[dotc] def isLegalTopLevelTree(tree: Tree): Boolean = tree match { + def isLegalTopLevelTree(tree: Tree): Boolean = tree match { case _: TypeApply | _: Apply | _: If | _: Match => true case _ => false } @@ -4164,7 +4166,7 @@ object Types { throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } if (tp.tree ne tree1) { - // Here is an example showing what would go wrong if we didn't re-assign types: + // Here is an example showing what would go wrong if we don't re-assign types: // Suppose we have // def f(x: Int) = x // def g(x: Int) = 2 * x From 1198a862953bf0bfb058e9b175f76c15505e0631 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 11:22:03 +0200 Subject: [PATCH 30/36] Factor TypeOf and Tree printers --- .../dotc/printing/DecompilerPrinter.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 74 ++++++++++++------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index fe03355800e5..927be407ad92 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -76,6 +76,6 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { override protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = { if (tree.symbol eq defn.QuotedExpr_apply) "'" else if (tree.symbol eq defn.QuotedType_apply) "'[" ~ toTextGlobal(tree.args, ", ") ~ "]" - else super.typeApplyText(tree) + else super.typeApplyText(tree.fun, tree.args) } } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c384f4068b3a..708474aed25e 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -200,21 +200,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { import tpd._ val underlying: Text = " <: " ~ toText(underlyingTp) provided ctx.settings.XprintTypes.value def treeText = tree match { - case TypeApply(fun, args) => toTextLocal(fun.tpe) ~ "[" ~ toTextGlobal(args.tpes, ", ") ~ "]" - case Apply(fun, args) => toTextLocal(fun.tpe) ~ "(" ~ toTextGlobal(args.tpes, ", ") ~ ")" + case TypeApply(fun, args) => + typeApplyText(fun.tpe, args.tpes) + case Apply(fun, args) => + applyText(fun.tpe, args.tpes) case If(cond, thenp, elsep) => - changePrec(GlobalPrec) { - keywordStr("if ") ~ toText(cond.tpe) ~ keywordText(" then ") ~ toText(thenp.tpe) ~ - (keywordStr(" else ") ~ toText(elsep.tpe) provided !elsep.isEmpty) - } + val elze = if (elsep.isEmpty) None else Some(elsep.tpe) + ifText(cond.tpe, thenp.tpe, elze) case Match(sel, cases) => - val blockTexts = cases map { case CaseDef(pat, guard, body) => - keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ - toText(List(body.tpe), "\n") - } - val blockText = ("{" ~ Text(blockTexts, "\n") ~ "}").close - if (sel.isEmpty) blockText - else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText } + matchText(sel, cases, showType = true) } return typeText("{ ") ~ inTypeOf { treeText } ~ underlying ~ typeText(" }") case tp: AnnotatedType if homogenizedView => @@ -254,7 +248,40 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ("{" ~ toText(trees, "\n") ~ "}").close protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = - toTextLocal(tree.fun) ~ "[" ~ toTextGlobal(tree.args, ", ") ~ "]" + typeApplyText(tree.fun, tree.args) + + protected def typeApplyText(fun: Showable, args: List[Showable]): Text = + toTextLocal(fun) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" + + protected def applyText(fun: Showable, args: List[Showable]): Text = + toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + + protected def ifText(cond: Showable, thenp: Showable, elsep: Option[Showable]): Text = + changePrec(GlobalPrec) ( + keywordStr("if ") + ~ cond.toText(this) + ~ (keywordText(" then") provided !cond.isInstanceOf[untpd.Parens]) + ~~ thenp.toText(this) + ~ elsep.map(keywordStr(" else ") ~ _.toText(this)).getOrElse("") + ) + + protected def caseDefText[T >: Untyped](cd: CaseDef[T], showType: Boolean): Text = { + val CaseDef(pat, guard, body) = cd + val bodyText = body match { + case body if showType => toText(List(body.asInstanceOf[tpd.Tree].tpe), "\n") + case Block(stats, expr) => toText(stats :+ expr, "\n") + case expr => toText(expr) + } + keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ bodyText + } + + protected def matchText[T >: Untyped](sel: Tree[T], cases: List[CaseDef[T]], showType: Boolean): Text = { + val selText = if (showType) toText(sel.asInstanceOf[tpd.Tree].tpe) else toText(sel) + if (sel.isEmpty) blockText(cases) + else changePrec(GlobalPrec) { selText ~ keywordStr(" match ") ~ + ("{" ~ Text(cases.map(c => caseDefText(c, showType)), "\n") ~ "}").close + } + } protected def toTextCore[T >: Untyped](tree: Tree[T]): Text = { import untpd.{modsDeco => _, _} @@ -266,11 +293,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def optDotPrefix(tree: This) = optText(tree.qual)(_ ~ ".") provided !isLocalThis(tree) - def caseBlockText(tree: Tree): Text = tree match { - case Block(stats, expr) => toText(stats :+ expr, "\n") - case expr => toText(expr) - } - // Dotty deviation: called with an untpd.Tree, so cannot be a untpd.Tree[T] (seems to be a Scala2 problem to allow this) // More deviations marked below as // DD def enumText(tree: untpd.Tree) = tree match { // DD @@ -334,7 +356,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { keywordStr("throw ") ~ toText(args.head) } else - toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + applyText(fun, args) case tree: TypeApply => typeApplyText(tree) case Literal(c) => @@ -362,17 +384,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case block: Block => blockToText(block) case If(cond, thenp, elsep) => - changePrec(GlobalPrec) { - keywordStr("if ") ~ toText(cond) ~ (keywordText(" then") provided !cond.isInstanceOf[Parens]) ~~ toText(thenp) ~ optText(elsep)(keywordStr(" else ") ~ _) - } + val elze = if (elsep.isEmpty) None else Some(elsep) + ifText(cond, thenp, elze) case Closure(env, ref, target) => "closure(" ~ (toTextGlobal(env, ", ") ~ " | " provided env.nonEmpty) ~ toTextGlobal(ref) ~ (":" ~ toText(target) provided !target.isEmpty) ~ ")" case Match(sel, cases) => - if (sel.isEmpty) blockText(cases) - else changePrec(GlobalPrec) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) } - case CaseDef(pat, guard, body) => - keywordStr("case ") ~ inPattern(toText(pat)) ~ optText(guard)(keywordStr(" if ") ~ _) ~ " => " ~ caseBlockText(body) + matchText(sel, cases, showType = false) + case cd: CaseDef => + caseDefText(cd, showType = false) case Return(expr, from) => changePrec(GlobalPrec) { keywordStr("return") ~ optText(expr)(" " ~ _) } case Try(expr, cases, finalizer) => From 004a69cfb9ffc457359bf1291130c0edb7e9e23c Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 11:53:33 +0200 Subject: [PATCH 31/36] Minor comment updates --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 9 +++++++-- compiler/src/dotty/tools/dotc/core/Types.scala | 12 ++++++------ .../src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- tests/neg/transparent.scala | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 5017508f8eaa..5056b7818392 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -785,9 +785,14 @@ object SymDenotations { def isTransparentMethod(implicit ctx: Context): Boolean = is(TransparentMethod, butNot = Accessor) - /** Does this symbol have a transparent owner, itself included? */ - def isTransitivelyTransparent(implicit ctx: Context): Boolean = + /** Are we in a transparent context? + * Either - this symbol has a transparent owner, itself included + * Or - we are inside a SingletonTypeTree + */ + def isTransitivelyTransparent(implicit ctx: Context): Boolean = { + // Note: Should we use a mode instead? ownersIterator.exists(_.isTransparentMethod) || ctx.outersIterator.exists(_.tree.isInstanceOf[SingletonTypeTree[_]]) + } def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 62a7c1661cb6..2edb2c927e65 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -87,12 +87,12 @@ object Types { // ----- Tests ----------------------------------------------------- // // debug only: a unique identifier for a type - val uniqId = { - nextId = nextId + 1 +// val uniqId = { +// nextId = nextId + 1 // if (nextId == 19555) // println("foo") - nextId - } +// nextId +// } /** A cache indicating whether the type was still provisional, last time we checked */ @sharable private var mightBeProvisional = true @@ -3932,7 +3932,7 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- /** */ - class TypeOf(val underlyingTp: Type, val tree: Tree, annot: Annotation) extends AnnotatedType(underlyingTp, annot) { + class TypeOf private (val underlyingTp: Type, val tree: Tree, annot: Annotation) extends AnnotatedType(underlyingTp, annot) { assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree in TypeOf: $tree") override def equals(that: Any): Boolean = { @@ -3967,7 +3967,7 @@ object Types { if ((parent eq this.parent) && (annot eq this.annot)) this else TypeOf(parent, annot.arguments.head) - override def toString(): String = s"TypeOf($underlyingTp, $tree)#$uniqId" + override def toString(): String = s"TypeOf($underlyingTp, $tree)" } object TypeOf { diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 236fceebbd5b..c2874d8a36b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -500,7 +500,7 @@ object TreeChecker { assert(tp.isInstantiated, s"Uninstantiated type variable: ${tp.show}") apply(tp.underlying) case tp: TypeOf => - assert(tp.tree.tpe eq NoType, s"A TypeOf's tree's type should point back to the TypeOf: $tp") + assert(tp.tree.tpe eq NoType, s"See note in TypeOf.apply") apply(tp.underlyingTp) mapOver(tp) case _ => diff --git a/tests/neg/transparent.scala b/tests/neg/transparent.scala index 67a26b6f4f56..b39889f2e947 100644 --- a/tests/neg/transparent.scala +++ b/tests/neg/transparent.scala @@ -4,7 +4,7 @@ object Invalid { f(1): {0} // error val y: Int = ??? - type YPlusOne = {y + 1} // error + type YPlusOne = {y + 1} // error: Non-sensical singleton-type expression: ... } // object SimpleEqs { From 4fb78f35151246923116e15f046c5f9aa5d05997 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 13:50:05 +0200 Subject: [PATCH 32/36] Remove derivedTypeOf as it's never used --- .../src/dotty/tools/dotc/core/Types.scala | 17 +---------- tests/neg/transparent.scala | 9 ++++++ tests/pos/transparent.scala | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2edb2c927e65..f5337419939e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3954,15 +3954,11 @@ object Types { false } } - compareTree(this.tree, that.tree) && { assert(this.underlyingTp == that.underlyingTp, s"UTP:\n\t${this.underlyingTp}\n\t${that.underlyingTp}"); true } + compareTree(this.tree, that.tree) case _ => false } } - def derivedTypeOf(underlyingTp: Type, tree: Tree)(implicit ctx: Context): TypeOf = - if ((this.tree eq tree) && (this.underlyingTp eq underlyingTp)) this - else TypeOf(underlyingTp, tree) - override def derivedAnnotatedType(parent: Type, annot: Annotation)(implicit ctx: Context): AnnotatedType = if ((parent eq this.parent) && (annot eq this.annot)) this else TypeOf(parent, annot.arguments.head) @@ -4059,8 +4055,6 @@ object Types { // note: currying needed because Scala2 does not support param-dependencies protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = tp.derivedLambdaType(tp.paramNames, formals, restpe) - protected def derivedTypeOf(tp: TypeOf, underlyingTp: Type, tree: Tree): Type = - tp.derivedTypeOf(underlyingTp, tree) /** Map this function over given type */ def mapOver(tp: Type): Type = { @@ -4446,15 +4440,6 @@ object Types { else tp.derivedAnnotatedType(underlying, annot) } - override protected def derivedTypeOf(tp: TypeOf, underlying: Type, tree: Tree) = - underlying match { - case Range(lo, hi) => - range(tp.derivedTypeOf(lo, tree), tp.derivedTypeOf(hi, tree)) - case _ => - if (underlying.isBottomType) underlying - else tp.derivedTypeOf(underlying, tree) - } - override protected def derivedWildcardType(tp: WildcardType, bounds: Type) = { tp.derivedWildcardType(rangeToBounds(bounds)) } diff --git a/tests/neg/transparent.scala b/tests/neg/transparent.scala index b39889f2e947..f04a251061ae 100644 --- a/tests/neg/transparent.scala +++ b/tests/neg/transparent.scala @@ -7,6 +7,15 @@ object Invalid { type YPlusOne = {y + 1} // error: Non-sensical singleton-type expression: ... } +object PrivateLeaks { + transparent def foo(x: Any): { x } = x + class A { + private transparent def bar(i: Int): Int = i + 1 + val a: { foo(bar(1)) } = foo(bar(1)) // error: non-private value a refers to private method bar + // in its type signature { PrivateLeaks.foo({ A.this.bar(1) }) } + } +} + // object SimpleEqs { // val x = 1 // val y: {x} = x diff --git a/tests/pos/transparent.scala b/tests/pos/transparent.scala index 1e15a11f598e..0a36bfebc63b 100644 --- a/tests/pos/transparent.scala +++ b/tests/pos/transparent.scala @@ -50,6 +50,34 @@ object Applied { val a: { foo2(true) } = foo2(true) } +object Approx1 { + transparent def foo(x: Any): { x } = x + class A { + transparent def bar(i: Int): Int = i + 1 + val v: { bar(foo(1)) } = bar(foo(1)) + } + + val a = new A {} + val b: { a.bar(foo(1)) } = a.v + + var c = new A {} + val d: { c.bar(foo(1)) } = c.v +} + +object Approx2 { + transparent def foo(x: Any): { x } = x + class A { + transparent def bar(i: Int): Int = i + 1 + val v: { foo(bar(1)) } = foo(bar(1)) + } + + val a = new A {} + val b: { foo(a.bar(1)) }= a.v + + val c = new A {} + val d: { foo(c.bar(1)) }= c.v +} + // object AvoidLocalRefs { // type Id[T] = T From f200e00783cf8f6d2dd5d07fe10ac843992e54e9 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 14:04:12 +0200 Subject: [PATCH 33/36] Document TypeOf class --- .../src/dotty/tools/dotc/core/Types.scala | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f5337419939e..9a14eccf2905 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3931,7 +3931,28 @@ object Types { // ----- TypeOf ------------------------------------------------------------------------- - /** */ + /** Type that represents the precise type of a given term. + * Precision is only kept for Apply, TypeApply, If and Match trees. + * + * The idea behind this type is to be able to compute more precise types + * when more information is available. + * + * TypeOfs are represented by an underlying type and a tree. The top level + * node of the tree must be one of the nodes mentioned above, and is only + * used as a "marker" node, meaning that we will never look at its type. + * + * In a sense, TypeOf types are isomorphic to the following 4 types: + * + * TypeOf(u, Apply(fun, args)) ~ SuspendedApply(u, fun, args) + * TypeOf(u, TypeApply(fun, args)) ~ SuspendedTypeApply(u, fun, args) + * TypeOf(u, If(cond, thenp, elsep)) ~ SuspendedIf(u, cond, thenp, elsep) + * TypeOf(u, Match(selector, cases)) ~ SuspendedMatch(u, selector, cases) + * + * Where u is the type that the tree would have had otherwise. + * + * It should be the case that whenever two TypeOfs are equal, so are their + * underlying types. + */ class TypeOf private (val underlyingTp: Type, val tree: Tree, annot: Annotation) extends AnnotatedType(underlyingTp, annot) { assert(TypeOf.isLegalTopLevelTree(tree), s"Illegal top-level tree in TypeOf: $tree") From bfbfaddb5ff1a85fa0193322565fef31ac6559a7 Mon Sep 17 00:00:00 2001 From: Georg Schmid Date: Fri, 15 Jun 2018 14:53:17 +0200 Subject: [PATCH 34/36] Remove non-sensical example in TypeMap documentation --- compiler/src/dotty/tools/dotc/core/Types.scala | 9 --------- 1 file changed, 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9a14eccf2905..e8af91ff15a4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4181,15 +4181,6 @@ object Types { throw new AssertionError(s"TypeOf shouldn't contain $tree as top-level node.") } if (tp.tree ne tree1) { - // Here is an example showing what would go wrong if we don't re-assign types: - // Suppose we have - // def f(x: Int) = x - // def g(x: Int) = 2 * x - // def h(f: Int => Int) = f(1) - // h(g): { 2 * 1 } - // Given a type map substituting f for g in f(1), the underlying - // type should be substituted to the result type of g(1), that is, - // 2 * 1. assert(!tp.underlyingTp.exists || tree1.tpe.exists, i"Derived TypeOf's type became NoType") tree1.tpe } else { From f5539bf9454547d4a5b77cf9d020c633abbc14df Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 15:43:01 +0200 Subject: [PATCH 35/36] Add missing This and Super in assignType(SingletonTypeTree) --- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 8c1e44a09b9c..0c6454bf7f6f 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -500,7 +500,7 @@ trait TypeAssigner { def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = { val tp = ref match { - case _: Literal | _: Ident | _: Select | _: Block => ref.tpe + case _: Literal | _: Ident | _: Select | _: Block | _: This | _: Super => ref.tpe case _ => if (TypeOf.isLegalTopLevelTree(ref)) if (ref.tpe.isInstanceOf[TypeOf]) From 27b16900a05f27cc89f45b5050b2c30e8bf71248 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 15 Jun 2018 16:05:21 +0200 Subject: [PATCH 36/36] Fix name clash betwen TypeOf and dotty.tools.repl.TypeOf --- compiler/src/dotty/tools/repl/ReplDriver.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index f279d44b7f8a..d7d58f965349 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -14,7 +14,7 @@ import dotc.core.Contexts.Context import dotc.{ CompilationUnit, Run } import dotc.core.Mode import dotc.core.Flags._ -import dotc.core.Types._ +import dotc.core.Types.{TypeOf => _, _} import dotc.core.StdNames._ import dotc.core.Names.Name import dotc.core.NameOps._