diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5f42c748bd33..e8c292399bf4 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -201,7 +201,7 @@ object desugar { case (vparam :: vparams) :: vparamss1 => def defaultGetter: DefDef = DefDef( - name = DefaultGetterName(meth.name, n), + name = DefaultGetterName(meth.name.asTermName, n), tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))), vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam), n), tpt = TypeTree(), @@ -357,7 +357,7 @@ object desugar { val classTycon: Tree = new TypeRefTree // watching is set at end of method def appliedTypeTree(tycon: Tree, args: List[Tree]) = - (if (args.isEmpty) tycon else AppliedTypeTree(tycon, args)) + (if (args.isEmpty) tycon else TypeApply(tycon, args)) .withPos(cdef.pos.startPos) def isHK(tparam: Tree): Boolean = tparam match { @@ -371,7 +371,7 @@ object desugar { val targ = refOfDef(tparam) def fullyApplied(tparam: Tree): Tree = tparam match { case TypeDef(_, LambdaTypeTree(tparams, body)) => - AppliedTypeTree(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree))) + TypeApply(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree))) case TypeDef(_, rhs: DerivedTypeTree) => fullyApplied(rhs.watched) case _ => @@ -1113,7 +1113,7 @@ object desugar { Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) case InfixOp(l, op, r) => if (ctx.mode is Mode.Type) - AppliedTypeTree(op, l :: r :: Nil) // op[l, r] + TypeApply(op, l :: r :: Nil) // op[l, r] else { assert(ctx.mode is Mode.Pattern) // expressions are handled separately by `binop` Apply(op, l :: r :: Nil) // op(l, r) @@ -1122,7 +1122,7 @@ object desugar { if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == tpnme.raw.STAR) { val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType Annotated( - AppliedTypeTree(ref(seqType), t), + TypeApply(ref(seqType), t :: Nil), New(ref(defn.RepeatedAnnotType), Nil :: Nil)) } else { assert(ctx.mode.isExpr || ctx.reporter.hasErrors || ctx.mode.is(Mode.Interactive), ctx.mode) @@ -1138,7 +1138,7 @@ object desugar { ctx.error(TupleTooLong(ts), tree.pos) unitLiteral } else if (arity == 1) ts.head - else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts) + else if (ctx.mode is Mode.Type) TypeApply(ref(tupleTypeRef), ts) else if (arity == 0) unitLiteral else Apply(ref(tupleTypeRef.classSymbol.companionModule.termRef), ts) case WhileDo(cond, body) => diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index ebb3157a5733..c140c9eb5588 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -143,7 +143,7 @@ object DesugarEnums { def extractType(t: Tree): Tree = t match { case Apply(t1, _) => extractType(t1) - case TypeApply(t1, ts) => AppliedTypeTree(extractType(t1), ts) + case TypeApply(t1, ts) => TypeApply(extractType(t1), ts) case Select(t1, nme.CONSTRUCTOR) => extractType(t1) case New(t1) => t1 case t1 => t1 diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 69c415ff7e66..d50f53703ddd 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -72,7 +72,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => */ def methPart(tree: Tree): Tree = stripApply(tree) match { case TypeApply(fn, _) => methPart(fn) - case AppliedTypeTree(fn, _) => methPart(fn) // !!! should not be needed case Block(stats, expr) => methPart(expr) case mp => mp } @@ -152,7 +151,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match { case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1) case tpt: TypeTree => tpt.typeOpt.isRepeatedParam - case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true + case TypeApply(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true case _ => false } @@ -164,7 +163,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind]) - case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind]) + case TypeApply(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind]) case Select(tpt, _) => mayBeTypePat(tpt) case Annotated(tpt, _) => mayBeTypePat(tpt) case _ => false @@ -240,6 +239,35 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case y => y } + /** The largest subset of {NoInits, PureInterface} that a + * trait or class enclosing this statement can have as flags. + */ + def defKind(tree: Tree)(implicit ctx: Context): FlagSet = unsplice(tree) match { + case EmptyTree | _: Import => NoInitsInterface + case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface + case tree: DefDef => + if (tree.unforcedRhs == EmptyTree && + tree.vparamss.forall(_.forall(_.rhs.isEmpty))) NoInitsInterface + else NoInits + case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags + case _ => EmptyFlags + } + + /** The largest subset of {NoInits, PureInterface} that a + * trait or class with these parents can have as flags. + */ + def parentsKind(parents: List[Tree])(implicit ctx: Context): FlagSet = parents match { + case Nil => NoInitsInterface + case Apply(_, _ :: _) :: _ => EmptyFlags + case _ :: parents1 => parentsKind(parents1) + } + + /** The largest subset of {NoInits, PureInterface} that a + * trait or class with this body can have as flags. + */ + def bodyKind(body: List[Tree])(implicit ctx: Context): FlagSet = + (NoInitsInterface /: body)((fs, stat) => fs & defKind(stat)) + /** Checks whether predicate `p` is true for all result parts of this expression, * where we zoom into Ifs, Matches, and Blocks. */ @@ -262,6 +290,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case _ => tree } + def isBounds(tree: Tree)(implicit ctx: Context) = tree match { + case tree: TypeBoundsTree => true + case TypedSplice(tree1) => tree1.tpe.isInstanceOf[TypeBounds] + case _ => false + } + /** True iff definition is a val or def with no right-hand-side, or it * is an abstract typoe declaration */ @@ -310,6 +344,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case _ => false } + /** Is this the RHS of a transparnt type def, which needs to be represented as a DefDef? */ + def isTypeDefRHS(tree: Tree): Boolean = tree match { + case tree: If => true + case _ => false + } + // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's } @@ -358,6 +398,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => refPurity(tree) case Select(qual, _) => refPurity(tree).min(exprPurity(qual)) + case New(_) => + SimplyPure case TypeApply(fn, _) => exprPurity(fn) /* @@ -369,13 +411,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case Apply(fn, args) => def isKnownPureOp(sym: Symbol) = sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass - // Note: After uncurry, field accesses are represented as Apply(getter, Nil), - // so an Apply can also be pure. - if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn) - else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)) - // A constant expression with pure arguments is pure. + if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol) + // A constant expression with pure arguments is pure. + || fn.symbol.isStable) minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure - else Impure + else + Impure case Typed(expr, _) => exprPurity(expr) case Block(stats, expr) => @@ -402,11 +443,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable * flags set. */ - def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = - if (!tree.tpe.widen.isParameterless || tree.symbol.is(Erased)) SimplyPure - else if (!tree.symbol.isStable) Impure - else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start. + def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = { + val sym = tree.symbol + if (!tree.hasType) Impure + else if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure + else if (!sym.isStable) Impure + else if (sym.is(Module)) + if (sym.moduleClass.isNoInitsClass) Pure else Idempotent + else if (sym.is(Lazy)) Idempotent else SimplyPure + } def isPureRef(tree: Tree)(implicit ctx: Context) = refPurity(tree) == SimplyPure @@ -623,17 +669,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => accum(Nil, root) } - /** The largest subset of {NoInits, PureInterface} that a - * trait enclosing this statement can have as flags. - */ - def defKind(tree: Tree): FlagSet = unsplice(tree) match { - case EmptyTree | _: Import => NoInitsInterface - case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface - case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits - case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags - case _ => EmptyFlags - } - /** The top level classes in this tree, including only those module classes that * are not a linked class of some other class in the result. */ diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index e52213cf0485..c75bdb2a9cf8 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole /** A map that applies three functions and a substitution together to a tree and * makes sure they are coordinated so that the result is well-typed. The functions are - * @param typeMap A function from Type to Type that gets applied to the + * @param typeMap A function from Type to Type that gets applied to the * type of every tree node and to all locally defined symbols, * followed by the substitution [substFrom := substTo]. * @param treeMap A transformer that translates all encountered subtrees in @@ -95,7 +95,9 @@ class TreeTypeMap( val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs)) res.symbol.transformAnnotations { - case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs) + case ann: BodyAnnotation => + if (res.symbol.isTransparentMethod) ann.derivedAnnotation(transform(ann.tree)) + else ann.derivedAnnotation(res.rhs) case ann => ann } res @@ -126,7 +128,7 @@ class TreeTypeMap( override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = transformDefs(trees)._2 - private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { + def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { val tmap = withMappedSyms(tpd.localSyms(trees)) (tmap, tmap.transformSub(trees)) } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 9f72feb216b9..63b02cf66a8a 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -434,11 +434,13 @@ object Trees { def forwardTo = qual } - abstract class GenericApply[-T >: Untyped] extends ProxyTree[T] with TermTree[T] { + abstract class GenericApply[-T >: Untyped] extends ProxyTree[T] { type ThisTree[-T >: Untyped] <: GenericApply[T] val fun: Tree[T] val args: List[Tree[T]] def forwardTo = fun + override def isTerm = fun.isTerm + override def isType = fun.isType } /** fun(args) */ @@ -492,8 +494,10 @@ object Trees { /** if cond then thenp else elsep */ case class If[-T >: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T]) - extends TermTree[T] { + extends Tree[T] { type ThisTree[-T >: Untyped] = If[T] + override def isTerm = thenp.isTerm + override def isType = thenp.isType } /** A closure with an environment and a reference to a method. @@ -630,13 +634,6 @@ object Trees { def forwardTo = tpt } - /** tpt[args] */ - case class AppliedTypeTree[-T >: Untyped] private[ast] (tpt: Tree[T], args: List[Tree[T]]) - extends ProxyTree[T] with TypTree[T] { - type ThisTree[-T >: Untyped] = AppliedTypeTree[T] - def forwardTo = tpt - } - /** [typeparams] -> tpt */ case class LambdaTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T]) extends TypTree[T] { @@ -697,8 +694,11 @@ object Trees { protected def force(x: AnyRef) = preRhs = x } - /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */ - case class DefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]], + /** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs + * or + * mods type[tparams](vparams_1)...(vparams_n): tpt = rhs + */ + case class DefDef[-T >: Untyped] private[ast] (name: Name, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], private var preRhs: LazyTree) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = DefDef[T] @@ -891,7 +891,6 @@ object Trees { type AndTypeTree = Trees.AndTypeTree[T] type OrTypeTree = Trees.OrTypeTree[T] type RefinedTypeTree = Trees.RefinedTypeTree[T] - type AppliedTypeTree = Trees.AppliedTypeTree[T] type LambdaTypeTree = Trees.LambdaTypeTree[T] type ByNameTypeTree = Trees.ByNameTypeTree[T] type TypeBoundsTree = Trees.TypeBoundsTree[T] @@ -1053,10 +1052,6 @@ object Trees { case tree: RefinedTypeTree if (tpt eq tree.tpt) && (refinements eq tree.refinements) => tree case _ => finalize(tree, untpd.RefinedTypeTree(tpt, refinements)) } - def AppliedTypeTree(tree: Tree)(tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { - case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree - case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) - } def LambdaTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): LambdaTypeTree = tree match { case tree: LambdaTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree case _ => finalize(tree, untpd.LambdaTypeTree(tparams, body)) @@ -1085,7 +1080,7 @@ object Trees { case tree: ValDef if (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree case _ => finalize(tree, untpd.ValDef(name, tpt, rhs)) } - def DefDef(tree: Tree)(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = tree match { + def DefDef(tree: Tree)(name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = tree match { case tree: DefDef if (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree case _ => finalize(tree, untpd.DefDef(name, tparams, vparamss, tpt, rhs)) } @@ -1109,6 +1104,10 @@ object Trees { case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree case _ => finalize(tree, untpd.Annotated(arg, annot)) } + def UntypedSplice(tree: Tree)(splice: untpd.Tree) = tree match { + case tree: tpd.UntypedSplice if tree.splice `eq` splice => tree + case _ => finalize(tree, tpd.UntypedSplice(splice)) + } def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match { case tree: Thicket if trees eq tree.trees => tree case _ => finalize(tree, untpd.Thicket(trees)) @@ -1128,7 +1127,7 @@ object Trees { UnApply(tree: Tree)(fun, implicits, patterns) def ValDef(tree: ValDef)(name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): ValDef = ValDef(tree: Tree)(name, tpt, rhs) - def DefDef(tree: DefDef)(name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): DefDef = + def DefDef(tree: DefDef)(name: Name = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): DefDef = DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs) def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs): TypeDef = TypeDef(tree: Tree)(name, rhs) @@ -1146,7 +1145,7 @@ object Trees { */ protected def inlineContext(call: Tree)(implicit ctx: Context): Context = ctx - abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { + abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self => def transform(tree: Tree)(implicit ctx: Context): Tree = { Stats.record(s"TreeMap.transform $getClass") @@ -1206,8 +1205,6 @@ object Trees { cpy.OrTypeTree(tree)(transform(left), transform(right)) case RefinedTypeTree(tpt, refinements) => cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) - case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) case LambdaTypeTree(tparams, body) => implicit val ctx = localCtx cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) @@ -1245,8 +1242,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case _ if ctx.reporter.errorsReported => - tree + case _ => + transformMoreCases(tree) } } @@ -1258,9 +1255,26 @@ object Trees { transform(tree).asInstanceOf[Tr] def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] = transform(trees).asInstanceOf[List[Tr]] + + protected def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match { + case tpd.UntypedSplice(usplice) => + // For a typed tree map: homomorphism on the untyped part with + // recursive mapping of typed splices. + // The case is overridden in UntypedTreeMap.## + val untpdMap = new untpd.UntypedTreeMap { + override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = tree match { + case untpd.TypedSplice(tsplice) => + untpd.cpy.TypedSplice(tree)(self.transform(tsplice).asInstanceOf[tpd.Tree]) + // the cast is safe, since the UntypedSplice case is overridden in UntypedTreeMap. + case _ => super.transform(tree) + } + } + cpy.UntypedSplice(tree)(untpdMap.transform(usplice)) + case _ if ctx.reporter.errorsReported => tree + } } - abstract class TreeAccumulator[X] { + abstract class TreeAccumulator[X] { self => // Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node. def apply(x: X, tree: Tree)(implicit ctx: Context): X @@ -1321,8 +1335,6 @@ object Trees { this(this(x, left), right) case RefinedTypeTree(tpt, refinements) => this(this(x, tpt), refinements) - case AppliedTypeTree(tpt, args) => - this(this(x, tpt), args) case LambdaTypeTree(tparams, body) => implicit val ctx = localCtx this(this(x, tparams), body) @@ -1355,14 +1367,29 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) => - // In interactive mode, errors might come from previous runs. - // In case of errors it may be that typed trees point to untyped ones. - // The IDE can still traverse inside such trees, either in the run where errors - // are reported, or in subsequent ones. - x + case _ => + foldMoreCases(x, tree) } } + + def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match { + case tpd.UntypedSplice(usplice) => + // For a typed tree accumulator: skip the untyped part and fold all typed splices. + // The case is overridden in UntypedTreeAccumulator. + val untpdAcc = new untpd.UntypedTreeAccumulator[X] { + override def apply(x: X, tree: untpd.Tree)(implicit ctx: Context): X = tree match { + case untpd.TypedSplice(tsplice) => self(x, tsplice) + case _ => foldOver(x, tree) + } + } + untpdAcc(x, usplice) + case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) => + // In interactive mode, errors might come from previous runs. + // In case of errors it may be that typed trees point to untyped ones. + // The IDE can still traverse inside such trees, either in the run where errors + // are reported, or in subsequent ones. + x + } } abstract class TreeTraverser extends TreeAccumulator[Unit] { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a9feeae2da99..371d24015734 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -20,6 +20,8 @@ import scala.io.Codec /** Some creators for typed trees */ object tpd extends Trees.Instance[Type] with TypedTreeInfo { + case class UntypedSplice(splice: untpd.Tree) extends Tree + private def ta(implicit ctx: Context) = ctx.typeAssigner def Ident(tp: NamedType)(implicit ctx: Context): Ident = @@ -104,7 +106,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Closure(Nil, call, targetTpt)) } - /** A closure whole anonymous function has the given method type */ + /** A closure whose anonymous function has the given method type */ def Lambda(tpe: MethodType, rhsFn: List[Tree] => Tree)(implicit ctx: Context): Block = { val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe) Closure(meth, tss => rhsFn(tss.head).changeOwner(ctx.owner, meth)) @@ -146,9 +148,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def RefinedTypeTree(parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context): Tree = ta.assignType(untpd.RefinedTypeTree(parent, refinements), parent, refinements, refineCls) - def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree = - ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args) - def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree = ta.assignType(untpd.ByNameTypeTree(result), result) @@ -324,7 +323,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case pre: ThisType => tp.isType || pre.cls.isStaticOwner || - tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls + tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough // and was spuriously triggered in case inner class would inherit from outer one // eg anonymous TypeMap inside TypeMap.andThen @@ -640,7 +639,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError - implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { + implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal { def isValue(implicit ctx: Context): Boolean = tree.isTerm && tree.tpe.widen.isValueType @@ -1022,14 +1021,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else (trees.head.tpe eq trees1.head.tpe) && sameTypes(trees.tail, trees1.tail) } - def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = { - if (isIdempotentExpr(tree)) within(tree) + def letBindUnless(level: TreeInfo.PurityLevel, tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = { + if (exprPurity(tree) >= level) within(tree) else { val vdef = SyntheticValDef(TempResultName.fresh(), tree) Block(vdef :: Nil, within(Ident(vdef.namedType))) } } + def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = + letBindUnless(TreeInfo.Idempotent, tree)(within) + def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = { Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 80e4365a943c..8c26f4aaacfe 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -24,8 +24,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice * @param owner The current owner at the time the tree was defined */ - abstract case class TypedSplice(tree: tpd.Tree)(val owner: Symbol) extends ProxyTree { - def forwardTo = tree + abstract case class TypedSplice(splice: tpd.Tree)(val owner: Symbol) extends ProxyTree { + def forwardTo = splice } object TypedSplice { @@ -286,7 +286,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { 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) - def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args) def LambdaTypeTree(tparams: List[TypeDef], body: Tree): LambdaTypeTree = new LambdaTypeTree(tparams, body) def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result) def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi) @@ -294,7 +293,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Alternative(trees: List[Tree]): Alternative = new Alternative(trees) def UnApply(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = new UnApply(fun, implicits, patterns) def ValDef(name: TermName, tpt: Tree, rhs: LazyTree): ValDef = new ValDef(name, tpt, rhs) - def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) + def DefDef(name: Name, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) def TypeDef(name: TypeName, rhs: Tree): TypeDef = new TypeDef(name, rhs) def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = new Template(constr, parents, self, body) def Import(expr: Tree, selectors: List[untpd.Tree]): Import = new Import(expr, selectors) @@ -309,9 +308,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { */ def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = { val (tycon, targs) = tpt match { - case AppliedTypeTree(tycon, targs) => + case TypeApply(tycon, targs) => (tycon, targs) - case TypedSplice(AppliedTypeTree(tycon, targs)) => + case TypedSplice(TypeApply(tycon, targs)) => (TypedSplice(tycon), targs map (TypedSplice(_))) case TypedSplice(tpt1: tpd.Tree) => val tycon = tpt1.tpe.typeConstructor @@ -337,9 +336,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case _ => Apply(tpt, Nil) } - def AppliedTypeTree(tpt: Tree, arg: Tree): AppliedTypeTree = - AppliedTypeTree(tpt, arg :: Nil) - def TypeTree(tpe: Type)(implicit ctx: Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe)) def unitLiteral = Literal(Constant(())) @@ -496,10 +492,14 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)) } + def TypedSplice(tree: Tree)(splice: tpd.Tree)(implicit ctx: Context) = tree match { + case tree: TypedSplice if splice `eq` tree.splice => tree + case _ => finalize(tree, untpd.TypedSplice(splice)) + } } abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + override def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(name, impl) => cpy.ModuleDef(tree)(name, transformSub(impl)) case ParsedTry(expr, handler, finalizer) => @@ -540,15 +540,17 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) + case tpd.UntypedSplice(splice) => + cpy.UntypedSplice(tree)(transform(splice)) case TypedSplice(_) => tree case _ => - super.transform(tree) + super.transformMoreCases(tree) } } - abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { - override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match { + abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { self => + override def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match { case ModuleDef(name, impl) => this(x, impl) case ParsedTry(expr, handler, finalizer) => @@ -589,10 +591,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(x, bounds), cxBounds) case PatDef(mods, pats, tpt, rhs) => this(this(this(x, pats), tpt), rhs) - case TypedSplice(tree) => - this(x, tree) + case TypedSplice(splice) => + this(x, splice) + case tpd.UntypedSplice(splice) => + this(x, splice) case _ => - super.foldOver(x, tree) + super.foldMoreCases(x, tree) } } diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index ea96d9dcd5d9..7b5f5656260a 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -97,9 +97,9 @@ object Config { */ final val checkHKApplications = false - /** If this flag is set, method types are checked for valid parameter references + /** If this flag is set, term lambda types are checked for valid parameter references */ - final val checkMethodTypes = false + final val checkTermLambdas = false /** If this flag is set, it is checked that TypeRefs don't refer directly * to themselves. diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index 0d38791efacc..db0aee43c12d 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -121,7 +121,7 @@ object Comments { tree match { case tree: untpd.DefDef => - val newName = ctx.freshNames.newName(tree.name, NameKinds.DocArtifactName) + val newName = ctx.freshNames.newName(tree.name.toTermName, NameKinds.DocArtifactName) untpd.DefDef(newName, tree.tparams, tree.vparamss, tree.tpt, tree.rhs) case _ => ctx.error(ProperDefinitionNotFound(), codePos) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f5cfb43745f8..53642ac4b1c2 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -55,10 +55,10 @@ class Definitions { ctx.newSymbol(owner, name, flags | Permanent, info) private def newClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, infoFn: ClassSymbol => Type) = - ctx.newClassSymbol(owner, name, flags | Permanent, infoFn) + ctx.newClassSymbol(owner, name, flags | Permanent | NoInits, infoFn) private def enterCompleteClassSymbol(owner: Symbol, name: TypeName, flags: FlagSet, parents: List[TypeRef], decls: Scope = newScope) = - ctx.newCompleteClassSymbol(owner, name, flags | Permanent, parents, decls).entered + ctx.newCompleteClassSymbol(owner, name, flags | Permanent | NoInits, parents, decls).entered private def enterTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) = scope.enter(newSymbol(cls, name, flags, TypeBounds.empty)) @@ -275,6 +275,7 @@ class Definitions { val cls = ctx.requiredClass("java.lang.Object") assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) + cls.setFlag(NoInits) // The companion object doesn't really exist, `NoType` is the general // technique to do that. Here we need to set it before completing @@ -1215,6 +1216,13 @@ class Definitions { for (m <- ScalaShadowingPackageClass.info.decls) ScalaPackageClass.enter(m) + // Temporary measure, as long as we do not read these classes from Tasty. + // Scala-2 classes don't have NoInits set even if they are pure. We override this + // for Product and Serializable so that case classes can be pure. A full solution + // requiers that we read all Scala code from Tasty. + ProductClass.setFlag(NoInits) + SerializableClass.setFlag(NoInits) + // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 2fc48608dfe6..752e28da522a 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -223,7 +223,7 @@ object Flags { /** A method symbol. */ final val Method = termFlag(7, "") - final val HigherKinded = typeFlag(7, "") + final val TypeMethod = typeFlag(7, "") /** A (term or type) parameter to a class or method */ final val Param = commonFlag(8, "") @@ -364,7 +364,7 @@ object Flags { final val DefaultMethod = termFlag(38, "") /** Labelled with `transparent` modifier */ - final val Transparent = termFlag(39, "transparent") + final val Transparent = commonFlag(39, "transparent") /** Symbol is an enum class or enum case (if used with case) */ final val Enum = commonFlag(40, "") @@ -456,7 +456,7 @@ object Flags { /** Flags that are not (re)set when completing the denotation */ final val FromStartFlags = Module | Package | Deferred | Method.toCommonFlags | - HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags | + Param | ParamAccessor.toCommonFlags | Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | NonMember | ImplicitCommon | Permanent | Synthetic | diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 020b884b22b3..fb5ace7a798b 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -97,4 +97,9 @@ object Mode { /** Read comments from definitions when unpickling from TASTY */ val ReadComments = newMode(21, "ReadComments") + /** We are typing the body of a transparent method */ + val TransparentBody = newMode(22, "TransparentBody") + + /** Suppress inlining transparent method applications */ + val NoInlineTransparent = newMode(23, "NoInlineTransparent") } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index ff7a3abed486..9d96e6f557b1 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -324,7 +324,6 @@ object StdNames { // Compiler utilized names val AnnotatedType: N = "AnnotatedType" - val AppliedTypeTree: N = "AppliedTypeTree" val ArrayAnnotArg: N = "ArrayAnnotArg" val Constant: N = "Constant" val ConstantType: N = "ConstantType" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 95301176d78f..813b67e1d061 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -167,11 +167,14 @@ object SymDenotations { /** Unset given flags(s) of this denotation */ final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } - /** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */ - final def setNoInitsFlags(flags: FlagSet): Unit = { - val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits - setFlag(flags & mask) - } + /** Set applicable flags in {NoInits, PureInterface} + * @param parentFlags The flags that match the class or trait's parents + * @param bodyFlags The flags that match the class or trait's body + */ + final def setNoInitsFlags(parentFlags: FlagSet, bodyFlags: FlagSet): Unit = + setFlag( + if (myFlags.is(Trait)) NoInitsInterface & bodyFlags // no parents are initialized from a trait + else NoInits & bodyFlags & parentFlags) private def isCurrent(fs: FlagSet) = fs <= ( @@ -599,6 +602,12 @@ object SymDenotations { final def isStable(implicit ctx: Context) = isType || !is(Erased) && (is(Stable) || !(is(UnstableValue) || info.isInstanceOf[ExprType])) + /** Is this a denotation of a class that does not have - either direct or inherited - + * initaliazion code? + */ + def isNoInitsClass(implicit ctx: Context) = + isClass && asClass.baseClasses.forall(_.is(NoInits)) + /** Is this a "real" method? A real method is a method which is: * - not an accessor * - not a label @@ -783,7 +792,8 @@ object SymDenotations { is(InlineMethod, butNot = Accessor) def isTransparentMethod(implicit ctx: Context): Boolean = - is(TransparentMethod, butNot = Accessor) + is(TransparentMethod, butNot = AccessorOrSynthetic) || + is(TypeMethod) def isInlineableMethod(implicit ctx: Context) = isInlinedMethod || isTransparentMethod diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 4efcb0648c12..b0fdad877d23 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -446,8 +446,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // of an `AndType` can lead to a cascade of subtyping checks // This twist is needed to make collection/generic/ParFactory.scala compile fourthTry || compareRefinedSlow - case tp1: HKTypeLambda => - // HKTypeLambdas do not have members. + case tp1: HKLambda => + // HKLambdas do not have members. fourthTry case _ => compareRefinedSlow || fourthTry @@ -534,16 +534,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { either(recur(tp1, tp21), recur(tp1, tp22)) || fourthTry case tp2: MethodType => def compareMethod = tp1 match { - case tp1: MethodType => compareMethodOrPoly(tp1, tp2) + case tp1: MethodType => compareLambdaTypes(tp1, tp2) case _ => false } compareMethod - case tp2: PolyType => - def comparePoly = tp1 match { - case tp1: PolyType => compareMethodOrPoly(tp1, tp2) + case tp2: LambdaType => // either HKTermLambda or PolyType + def compareLambda = tp1 match { + case tp1: LambdaType if tp1.companion `eq` tp2.companion => compareLambdaTypes(tp1, tp2) case _ => false } - comparePoly + compareLambda case tp2 @ ExprType(restpe2) => def compareExpr = tp1 match { // We allow ()T to be a subtype of => T. @@ -582,7 +582,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { fourthTry } - def compareMethodOrPoly(tp1: MethodOrPoly, tp2: MethodOrPoly) = + def compareLambdaTypes(tp1: LambdaType, tp2: LambdaType) = (tp1.signature consistentParams tp2.signature) && matchingParams(tp1, tp2) && (!tp2.isImplicitMethod || tp1.isImplicitMethod) && @@ -1177,7 +1177,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Do lambda types `lam1` and `lam2` have parameters that have the same types * and the same implicit status? (after renaming one set to the other) */ - def matchingParams(lam1: MethodOrPoly, lam2: MethodOrPoly): Boolean = { + def matchingParams(lam1: LambdaType, lam2: LambdaType): Boolean = { /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match { case formal1 :: rest1 => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index b90a5bde2c98..ab806b852203 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -993,6 +993,21 @@ object Types { case _ => this } + /** If this type is a typeref or applied type referring to a TypeMethid with a type + * lambda as alias or upper bound, widen to the lambda. + */ + final def toLambda(implicit ctx: Context): Type = { + def isLambda(tp: Type): Boolean = tp match { + case tp: TypeRef => tp.symbol.is(TypeMethod) + case tp: AppliedType => isLambda(tp.tycon) + case _ => false + } + this match { + case tp: TypeProxy if isLambda(tp) && tp.superType.isInstanceOf[LambdaType] => tp.superType + case _ => this + } + } + /** If this type contains embedded union types, replace them by their joins. * "Embedded" means: inside intersectons or recursive types, or in prefixes of refined types. * If an embedded union is found, we first try to simplify or eliminate it by @@ -2955,6 +2970,16 @@ object Types { extends LambdaTypeCompanion[TermName, Type, LT] { def toPInfo(tp: Type)(implicit ctx: Context): Type = tp def syntheticParamName(n: Int) = nme.syntheticParamName(n) + + def checkValid(mt: TermLambda)(implicit ctx: Context): mt.type = { + if (Config.checkTermLambdas) + for ((paramInfo, idx) <- mt.paramInfos.zipWithIndex) + paramInfo.foreachPart { + case TermParamRef(`mt`, j) => assert(j < idx, mt) + case _ => + } + mt + } } abstract class TypeLambdaCompanion[LT <: TypeLambda] @@ -2967,6 +2992,8 @@ object Types { } abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] { self => + final def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp, self))) /** Produce method type from parameter symbols, with special mappings for repeated * and inline parameters: @@ -2987,19 +3014,6 @@ object Types { tl => params.map(p => tl.integrate(params, paramInfo(p))), tl => tl.integrate(params, resultType)) } - - final def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp, self))) - - def checkValid(mt: MethodType)(implicit ctx: Context): mt.type = { - if (Config.checkMethodTypes) - for ((paramInfo, idx) <- mt.paramInfos.zipWithIndex) - paramInfo.foreachPart { - case TermParamRef(`mt`, j) => assert(j < idx, mt) - case _ => - } - mt - } } object MethodType extends MethodTypeCompanion { @@ -3079,6 +3093,20 @@ object Types { protected def prefixString = "HKTypeLambda" } + class HKTermLambda(val paramNames: List[TermName])( + paramInfosExp: HKTermLambda => List[Type], resultTypeExp: HKTermLambda => Type) + extends HKLambda with TermLambda { + type This = HKTermLambda + def companion = HKTermLambda + + val paramInfos: List[Type] = paramInfosExp(this) + val resType: Type = resultTypeExp(this) + + assert(resType.isInstanceOf[TermType], this) + + protected def prefixString = "HKTermLambda" + } + /** The type of a polymorphic method. It has the same form as HKTypeLambda, * except it applies to terms and parameters do not have variances. */ @@ -3118,22 +3146,7 @@ object Types { protected def prefixString = "PolyType" } - object HKTypeLambda extends TypeLambdaCompanion[HKTypeLambda] { - def apply(paramNames: List[TypeName])( - paramInfosExp: HKTypeLambda => List[TypeBounds], - resultTypeExp: HKTypeLambda => Type)(implicit ctx: Context): HKTypeLambda = { - unique(new HKTypeLambda(paramNames)(paramInfosExp, resultTypeExp)) - } - - def unapply(tl: HKTypeLambda): Some[(List[LambdaParam], Type)] = - Some((tl.typeParams, tl.resType)) - - def any(n: Int)(implicit ctx: Context) = - apply(syntheticParamNames(n))( - pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) - - override def paramName(param: ParamInfo.Of[TypeName])(implicit ctx: Context): TypeName = - param.paramName.withVariance(param.paramVariance) + trait HKCompanion[N <: Name, PInfo <: Type, LT <: LambdaType] extends LambdaTypeCompanion[N, PInfo, LT] { /** Distributes Lambda inside type bounds. Examples: * @@ -3141,7 +3154,7 @@ object Types { * type T[X] <: U becomes type T >: Nothign <: ([X] -> U) * type T[X] >: L <: U becomes type T >: ([X] -> L) <: ([X] -> U) */ - override def fromParams[PI <: ParamInfo.Of[TypeName]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = { + override def fromParams[PI <: ParamInfo.Of[N]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = { def expand(tp: Type) = super.fromParams(params, tp) resultType match { case rt: TypeAlias => @@ -3155,6 +3168,32 @@ object Types { } } + object HKTypeLambda extends TypeLambdaCompanion[HKTypeLambda] with HKCompanion[TypeName, TypeBounds, HKTypeLambda] { + def apply(paramNames: List[TypeName])( + paramInfosExp: HKTypeLambda => List[TypeBounds], + resultTypeExp: HKTypeLambda => Type)(implicit ctx: Context): HKTypeLambda = { + unique(new HKTypeLambda(paramNames)(paramInfosExp, resultTypeExp)) + } + + def unapply(tl: HKTypeLambda): Some[(List[LambdaParam], Type)] = + Some((tl.typeParams, tl.resType)) + + def any(n: Int)(implicit ctx: Context) = + apply(syntheticParamNames(n))( + pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) + + override def paramName(param: ParamInfo.Of[TypeName])(implicit ctx: Context): TypeName = + param.paramName.withVariance(param.paramVariance) + } + + object HKTermLambda extends TermLambdaCompanion[HKTermLambda] with HKCompanion[TermName, Type, HKTermLambda] { + def apply(paramNames: List[TermName])( + paramInfosExp: HKTermLambda => List[Type], + resultTypeExp: HKTermLambda => Type)(implicit ctx: Context): HKTermLambda = { + checkValid(unique(new HKTermLambda(paramNames)(paramInfosExp, resultTypeExp))) + } + } + object PolyType extends TypeLambdaCompanion[PolyType] { def apply(paramNames: List[TypeName])( paramInfosExp: PolyType => List[TypeBounds], diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index f1237189459c..877dcea21a1b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -58,7 +58,12 @@ Standard-Section: "ASTs" TopLevelStat* VALDEF Length NameRef type_Term rhs_Term? Modifier* DEFDEF Length NameRef TypeParam* Params* returnType_Term rhs_Term? Modifier* - TYPEDEF Length NameRef (type_Term | Template) Modifier* + TYPEDEF Length NameRef + ( type_Term + | Template + | TypeParam* Params* returnType_Term rhs_Term + ) Modifier* + OBJECTDEF Length NameRef Template Modifier* IMPORT Length qual_Term Selector* Selector = IMPORTED name_NameRef RENAMED to_NameRef @@ -99,7 +104,8 @@ Standard-Section: "ASTs" TopLevelStat* SELECTtpt NameRef qual_Term SINGLETONtpt ref_Term REFINEDtpt Length underlying_Term refinement_Stat* - APPLIEDtpt Length tycon_Term arg_Term* + APPLYtpt Length tycon_Term arg_Term* + TYPEAPPLYtpt Length tycon_Term arg_Term* POLYtpt Length TypeParam* body_Term TYPEBOUNDStpt Length low_Term high_Term? ANNOTATEDtpt Length underlying_Term fullAnnotation_Term @@ -109,6 +115,7 @@ Standard-Section: "ASTs" TopLevelStat* EMPTYTREE SHAREDterm term_ASTRef HOLE Length idx_Nat arg_Tree* + UNTYPEDSPLICE Length splice_TermUntyped splice_Type CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? ImplicitArg = IMPLICITARG arg_Term @@ -203,6 +210,14 @@ Standard-Section: "ASTs" TopLevelStat* Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term +// --------------- untyped additions ------------------------------------------ + + TermUntyped = Term + TYPEDSPLICE Length splice_Term + FUNCTION Length body_Term arg_Term* + INFIXOP Length op_NameRef left_Term right_Term + PATDEF Length type_Term rhs_Term pattern_Term* Modifier* + Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way. Category 1 (tags 1-49) : tag @@ -233,7 +248,7 @@ Standard Section: "Comments" Comment* object TastyFormat { final val header = Array(0x5C, 0xA1, 0xAB, 0x1F) - val MajorVersion = 9 + val MajorVersion = 10 val MinorVersion = 0 /** Tags used to serialize names */ @@ -307,6 +322,7 @@ object TastyFormat { final val MACRO = 34 final val ERASED = 35 final val PARAMsetter = 36 + final val EMPTYTREE = 37 // Cat. 2: tag Nat @@ -394,7 +410,7 @@ object TastyFormat { final val REFINEDtype = 158 final val REFINEDtpt = 159 final val APPLIEDtype = 160 - final val APPLIEDtpt = 161 + final val TYPEAPPLYtpt = 161 final val TYPEBOUNDS = 162 final val TYPEBOUNDStpt = 163 final val ANDtype = 164 @@ -408,6 +424,7 @@ object TastyFormat { final val ANNOTATION = 172 final val TERMREFin = 173 final val TYPEREFin = 174 + final val APPLYtpt = 175 // In binary: 101100EI // I = implicit method type @@ -416,6 +433,15 @@ object TastyFormat { final val IMPLICITMETHODtype = 177 final val ERASEDMETHODtype = 178 final val ERASEDIMPLICITMETHODtype = 179 + final val OBJECTDEF = 180 + + final val UNTYPEDSPLICE = 199 + + // Tags for untyped trees only: + final val TYPEDSPLICE = 200 + final val FUNCTION = 201 + final val INFIXOP = 202 + final val PATDEF = 203 def methodType(isImplicit: Boolean = false, isErased: Boolean = false) = { val implicitOffset = if (isImplicit) 1 else 0 @@ -432,7 +458,7 @@ object TastyFormat { /** Useful for debugging */ def isLegalTag(tag: Int) = - firstSimpleTreeTag <= tag && tag <= PARAMsetter || + firstSimpleTreeTag <= tag && tag <= EMPTYTREE || firstNatTreeTag <= tag && tag <= SYMBOLconst || firstASTTreeTag <= tag && tag <= SINGLETONtpt || firstNatASTTreeTag <= tag && tag <= NAMEDARG || @@ -483,7 +509,8 @@ object TastyFormat { | SELECTtpt | SINGLETONtpt | REFINEDtpt - | APPLIEDtpt + | APPLYtpt + | TYPEAPPLYtpt | LAMBDAtpt | TYPEBOUNDStpt | ANNOTATEDtpt @@ -529,6 +556,7 @@ object TastyFormat { case DEFAULTparameterized => "DEFAULTparameterized" case STABLE => "STABLE" case PARAMsetter => "PARAMsetter" + case EMPTYTREE => "EMPTYTREE" case SHAREDterm => "SHAREDterm" case SHAREDtype => "SHAREDtype" @@ -560,6 +588,7 @@ object TastyFormat { case VALDEF => "VALDEF" case DEFDEF => "DEFDEF" case TYPEDEF => "TYPEDEF" + case OBJECTDEF => "OBJECTDEF" case IMPORT => "IMPORT" case TYPEPARAM => "TYPEPARAM" case PARAMS => "PARAMS" @@ -605,7 +634,8 @@ object TastyFormat { case REFINEDtype => "REFINEDtype" case REFINEDtpt => "REFINEDtpt" case APPLIEDtype => "APPLIEDtype" - case APPLIEDtpt => "APPLIEDtpt" + case APPLYtpt => "APPLYtpt" + case TYPEAPPLYtpt => "TYPEAPPLYtpt" case TYPEBOUNDS => "TYPEBOUNDS" case TYPEBOUNDStpt => "TYPEBOUNDStpt" case TYPEALIAS => "TYPEALIAS" @@ -627,13 +657,19 @@ object TastyFormat { case PRIVATEqualified => "PRIVATEqualified" case PROTECTEDqualified => "PROTECTEDqualified" case HOLE => "HOLE" + + case UNTYPEDSPLICE => "UNTYPEDSPLICE" + case TYPEDSPLICE => "TYPEDSPLICE" + case FUNCTION => "FUNCTION" + case INFIXOP => "INFIXOP" + case PATDEF => "PATDEF" } /** @return If non-negative, the number of leading references (represented as nats) of a length/trees entry. * If negative, minus the number of leading non-reference trees. */ def numRefs(tag: Int) = tag match { - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | + case VALDEF | DEFDEF | TYPEDEF | OBJECTDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | HOLE => 1 case RENAMED | PARAMtype => 2 case POLYtype | METHODtype | TYPELAMBDAtype => -1 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 3a0442d7cbc6..80a7aac1743b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -64,7 +64,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { tag match { case RENAMED => printName(); printName() - case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => + case VALDEF | DEFDEF | TYPEDEF | OBJECTDEF | TYPEPARAM | PARAM | NAMEDARG | BIND => printName(); printTrees() case REFINEDtype | TERMREFin | TYPEREFin => printName(); printTree(); 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..17651cc4f3e3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -4,7 +4,7 @@ package core package tasty import ast.Trees._ -import ast.{untpd, tpd} +import ast.{untpd, tpd, desugar} import TastyFormat._ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ import collection.mutable @@ -365,18 +365,15 @@ class TreePickler(pickler: TastyPickler) { writeByte(THROW) pickleTree(args.head) } else { - writeByte(APPLY) + writeByte(if (tree.isType) APPLYtpt else APPLY) withLength { pickleTree(fun) args.foreach(pickleTree) } } case TypeApply(fun, args) => - writeByte(TYPEAPPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTpt) - } + writeByte(if (tree.isType) TYPEAPPLYtpt else TYPEAPPLY) + withLength { pickleTree(fun); args.foreach(pickleTpt) } case Literal(const1) => pickleConstant { tree.tpe match { @@ -469,7 +466,8 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleParams(vparams) } } } - pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) + val tag = if (tree.symbol.isTerm) DEFDEF else TYPEDEF + pickleDef(tag, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) case tree: TypeDef => pickleDef(TYPEDEF, tree.symbol, tree.rhs) case tree: Template => @@ -522,9 +520,6 @@ class TreePickler(pickler: TastyPickler) { refinements.foreach(preRegister) withLength { pickleTree(parent); refinements.foreach(pickleTree) } } - case AppliedTypeTree(tycon, args) => - writeByte(APPLIEDtpt) - withLength { pickleTree(tycon); args.foreach(pickleTree) } case AndTypeTree(tp1, tp2) => writeByte(ANDtpt) withLength { pickleTree(tp1); pickleTree(tp2) } @@ -536,7 +531,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(tp) case Annotated(tree, annot) => writeByte(ANNOTATEDtpt) - withLength { pickleTree(tree); pickleTree(annot.tree) } + withLength { pickleTree(tree); pickleTree(annot) } case LambdaTypeTree(tparams, body) => writeByte(LAMBDAtpt) withLength { pickleParams(tparams); pickleTree(body) } @@ -546,6 +541,9 @@ class TreePickler(pickler: TastyPickler) { pickleTree(lo); if (hi ne lo) pickleTree(hi) } + case tpd.UntypedSplice(splice) => + writeByte(UNTYPEDSPLICE) + withLength { pickleUntyped(splice); pickleType(tree.tpe) } case Hole(idx, args) => writeByte(HOLE) withLength { @@ -577,15 +575,24 @@ class TreePickler(pickler: TastyPickler) { def pickleModifiers(sym: Symbol)(implicit ctx: Context): Unit = { import Flags._ - val flags = sym.flags + var flags = sym.flags val privateWithin = sym.privateWithin if (privateWithin.exists) { writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) pickleType(privateWithin.typeRef) + flags = flags &~ Protected } + if ((flags is ParamAccessor) && sym.isTerm && !sym.isSetter) + flags = flags &~ ParamAccessor // we only generate a tag for parameter setters + pickleFlags(flags, sym.isTerm) + sym.annotations.foreach(pickleAnnotation(sym, _)) + } + + def pickleFlags(flags: Flags.FlagSet, isTerm: Boolean)(implicit ctx: Context): Unit = { + import Flags._ if (flags is Private) writeByte(PRIVATE) - if (flags is Protected) if (!privateWithin.exists) writeByte(PROTECTED) - if ((flags is Final) && !(sym is Module)) writeByte(FINAL) + if (flags is Protected) writeByte(PROTECTED) + if (flags.is(Final, butNot = Module)) writeByte(FINAL) if (flags is Case) writeByte(CASE) if (flags is Override) writeByte(OVERRIDE) if (flags is Inline) writeByte(INLINE) @@ -598,7 +605,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is Synthetic) writeByte(SYNTHETIC) if (flags is Artifact) writeByte(ARTIFACT) if (flags is Scala2x) writeByte(SCALA2X) - if (sym.isTerm) { + if (isTerm) { if (flags is Implicit) writeByte(IMPLICIT) if (flags is Erased) writeByte(ERASED) if (flags.is(Lazy, butNot = Module)) writeByte(LAZY) @@ -608,7 +615,7 @@ class TreePickler(pickler: TastyPickler) { if (flags is CaseAccessor) writeByte(CASEaccessor) if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) if (flags is Stable) writeByte(STABLE) - if ((flags is ParamAccessor) && sym.isSetter) writeByte(PARAMsetter) + if (flags is ParamAccessor) writeByte(PARAMsetter) if (flags is Label) writeByte(LABEL) } else { if (flags is Sealed) writeByte(SEALED) @@ -617,7 +624,6 @@ class TreePickler(pickler: TastyPickler) { if (flags is Covariant) writeByte(COVARIANT) if (flags is Contravariant) writeByte(CONTRAVARIANT) } - sym.annotations.foreach(pickleAnnotation(sym, _)) } private def isUnpicklable(owner: Symbol, ann: Annotation)(implicit ctx: Context) = ann match { @@ -626,8 +632,8 @@ class TreePickler(pickler: TastyPickler) { // a different toplevel class, it is impossible to pickle a reference to it. // Such annotations will be reconstituted when unpickling the child class. // See tests/pickling/i3149.scala - case _ => ann.symbol == defn.BodyAnnot - // inline bodies are reconstituted automatically when unpickling + case _ => ann.symbol == defn.BodyAnnot && owner.isInlinedMethod + // bodies of inlined (but not transparent) methods are reconstituted automatically when unpickling } def pickleAnnotation(owner: Symbol, ann: Annotation)(implicit ctx: Context) = @@ -636,6 +642,229 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(ann.symbol.typeRef); pickleTree(ann.tree) } } +// ---- pickling untyped trees ---------------------------------- + + def pickleUntyped(tree: untpd.Tree)(implicit ctx: Context): Unit = { + + def pickleDummyRef(): Unit = writeNat(0) + + def pickleDummyType(): Unit = { + writeByte(SHAREDtype) + pickleDummyRef() + } + + def pickleUnlessEmpty(tree: untpd.Tree): Unit = + if (!tree.isEmpty) pickleUntyped(tree) + + def pickleTpt(tree: untpd.Tree) = pickleUntyped(tree)(ctx.addMode(Mode.Type)) + def pickleTerm(tree: untpd.Tree) = pickleUntyped(tree)(ctx.retractMode(Mode.Type)) + + def pickleAllParams(tree: untpd.DefDef): Unit = { + pickleParams(tree.tparams) + for (vparams <- tree.vparamss) { + writeByte(PARAMS) + withLength { pickleParams(vparams) } + } + } + + def pickleParams(trees: List[untpd.Tree]): Unit = + trees.foreach(pickleParam) + + def pickleParam(tree: untpd.Tree): Unit = tree match { + case tree: untpd.ValDef => pickleDef(PARAM, tree, tree.tpt) + case tree: untpd.DefDef => pickleDef(PARAM, tree, tree.tpt, tree.rhs) + case tree: untpd.TypeDef => pickleDef(TYPEPARAM, tree, tree.rhs) + } + + def pickleParent(tree: untpd.Tree): Unit = tree match { + case _: untpd.Apply | _: untpd.TypeApply => pickleUntyped(tree) + case _ => pickleTpt(tree) + } + + def pickleDef(tag: Int, tree: untpd.MemberDef, tpt: untpd.Tree, rhs: untpd.Tree = untpd.EmptyTree, pickleParams: => Unit = ()) = { + import untpd.modsDeco + writeByte(tag) + withLength { + pickleName(tree.name) + pickleParams + pickleTpt(tpt) + pickleUnlessEmpty(rhs) + pickleModifiers(tree.mods, tree.name.isTermName) + } + } + + def pickleModifiers(mods: untpd.Modifiers, isTerm: Boolean): Unit = { + import Flags._ + var flags = mods.flags + val privateWithin = mods.privateWithin + if (!privateWithin.isEmpty) { + writeByte(if (flags is Protected) PROTECTEDqualified else PRIVATEqualified) + pickleUntyped(untpd.Ident(privateWithin)) + flags = flags &~ Protected + } + pickleFlags(flags, isTerm) + mods.annotations.foreach(pickleAnnotation) + } + + def pickleAnnotation(annotTree: untpd.Tree) = { + writeByte(ANNOTATION) + withLength { pickleDummyType(); pickleUntyped(annotTree) } + } + + try tree match { + case Ident(name) => + writeByte(if (name.isTypeName) TYPEREF else TERMREF) + pickleName(name) + pickleDummyType() + case This(qual) => + writeByte(QUALTHIS) + pickleUntyped(qual) + case Select(qual, name) => + writeByte(if (name.isTypeName) SELECTtpt else SELECT) + pickleName(name) + if (qual.isType) pickleTpt(qual) else pickleTerm(qual) + case Apply(fun, args) => + writeByte(APPLY) + withLength { + pickleUntyped(fun) + args.foreach(pickleUntyped) + } + case untpd.Throw(exc) => + writeByte(THROW) + pickleUntyped(exc) + case TypeApply(fun, args) => + writeByte(TYPEAPPLY) + withLength { + pickleUntyped(fun) + args.foreach(pickleTpt) + } + case Literal(const) => + pickleConstant(const) + case Super(qual, mix) => + writeByte(SUPER) + withLength { + pickleUntyped(qual); + if (!mix.isEmpty) pickleUntyped(mix) + } + case New(tpt) => + writeByte(NEW) + pickleTpt(tpt) + case Typed(expr, tpt) => + writeByte(TYPED) + withLength { pickleUntyped(expr); pickleTpt(tpt) } + case NamedArg(name, arg) => + writeByte(NAMEDARG) + pickleName(name) + pickleUntyped(arg) + case Assign(lhs, rhs) => + writeByte(ASSIGN) + withLength { pickleUntyped(lhs); pickleUntyped(rhs) } + case Block(stats, expr) => + writeByte(BLOCK) + withLength { pickleUntyped(expr); stats.foreach(pickleUntyped) } + case If(cond, thenp, elsep) => + writeByte(IF) + withLength { pickleUntyped(cond); pickleUntyped(thenp); pickleUntyped(elsep) } + case Match(selector, cases) => + writeByte(MATCH) + withLength { pickleUntyped(selector); cases.foreach(pickleUntyped) } + case CaseDef(pat, guard, rhs) => + writeByte(CASEDEF) + withLength { pickleUntyped(pat); pickleUntyped(rhs); pickleUnlessEmpty(guard) } + case Return(expr, from) => + writeByte(RETURN) + withLength { pickleDummyRef(); pickleUnlessEmpty(expr) } + case Try(block, cases, finalizer) => + writeByte(TRY) + withLength { pickleUntyped(block); cases.foreach(pickleUntyped); pickleUnlessEmpty(finalizer) } + case Bind(name, body) => + writeByte(BIND) + withLength { + pickleName(name); pickleDummyType(); pickleUntyped(body) + } + case Alternative(alts) => + writeByte(ALTERNATIVE) + withLength { alts.foreach(pickleUntyped) } + case tree: untpd.ValDef => + pickleDef(VALDEF, tree, tree.tpt, tree.rhs) + case tree: untpd.DefDef => + pickleDef(DEFDEF, tree, tree.tpt, tree.rhs, pickleAllParams(tree)) + case tree: untpd.TypeDef => + pickleDef(TYPEDEF, tree, tree.rhs) + case tree: untpd.ModuleDef => + pickleDef(OBJECTDEF, tree, tree.impl) + case tree: untpd.Template => + writeByte(TEMPLATE) + tree.parents.foreach(pickleParent) + if (!tree.self.isEmpty) { + writeByte(SELFDEF); pickleName(tree.self.name); pickleTpt(tree.self.tpt) + } + pickleUntyped(tree.constr) + tree.body.foreach(pickleUntyped) + case Import(expr, selectors) => + writeByte(IMPORT) + withLength { pickleUntyped(expr); pickleSelectors(selectors) } + case tree: untpd.TypeTree => + pickleDummyType() + case SingletonTypeTree(ref) => + writeByte(SINGLETONtpt) + pickleTerm(ref) + case RefinedTypeTree(parent, refinements) => + writeByte(REFINEDtpt) + withLength { pickleTpt(parent); refinements.foreach(pickleTerm) } + case AndTypeTree(tp1, tp2) => + writeByte(ANDtpt) + withLength { pickleTpt(tp1); pickleTpt(tp2) } + case OrTypeTree(tp1, tp2) => + writeByte(ORtpt) + withLength { pickleTpt(tp1); pickleTpt(tp2) } + case ByNameTypeTree(tp) => + writeByte(BYNAMEtpt) + pickleTpt(tp) + case Annotated(tree, annot) => + writeByte(ANNOTATEDtpt) + withLength { pickleTpt(tree); pickleTerm(annot) } + case LambdaTypeTree(tparams, body) => + writeByte(LAMBDAtpt) + withLength { pickleParams(tparams); pickleTpt(body) } + case TypeBoundsTree(lo, hi) => + writeByte(TYPEBOUNDStpt) + withLength { + pickleTpt(lo); + if (hi ne lo) pickleTpt(hi) + } + case untpd.Function(args, body) => + writeByte(FUNCTION) + withLength { pickleUntyped(body); args.foreach(pickleUntyped) } + case untpd.InfixOp(l, op, r) => + writeByte(INFIXOP) + withLength { pickleUntyped(l); pickleUntyped(op); pickleUntyped(r) } + case untpd.PatDef(mods, pats, tpt, rhs) => + writeByte(PATDEF) + withLength { + pickleTpt(tpt) + pickleUntyped(rhs) + pats.foreach(pickleUntyped) + pickleModifiers(mods, isTerm = true) + } + case untpd.TypedSplice(splice) => + writeByte(TYPEDSPLICE) + withLength { pickleTree(splice) } + case Thicket(trees) => + if (trees.isEmpty) writeByte(EMPTYTREE) + else trees.foreach(pickleUntyped) + case _ => + pickleUntyped(desugar(tree)) + } + catch { + case ex: AssertionError => + println(i"error when pickling tree $tree") + throw ex + } + } + +// ---- main entry points --------------------------------------- + def pickle(trees: List[Tree])(implicit ctx: Context) = { trees.foreach(tree => if (!tree.isEmpty) pickleTree(tree)) def missing = forwardSymRefs.keysIterator.map(_.showLocated).toList diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 74378066b9b7..1e514c6b22f7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -350,7 +350,7 @@ class TreeUnpickler(reader: TastyReader, case binder: LambdaType => binder.paramRefs(readNat()) } case HOLE => - readHole(end, isType = true).tpe + readHole(end).tpe } assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") result @@ -438,6 +438,7 @@ class TreeUnpickler(reader: TastyReader, var flags = givenFlags if (lacksDefinition && tag != PARAM) flags |= Deferred if (tag == DEFDEF) flags |= Method + if (tag == TYPEDEF && givenFlags.is(Transparent)) flags |= TypeMethod if (givenFlags is Module) flags = flags | (if (tag == VALDEF) ModuleValCreationFlags else ModuleClassCreationFlags) if (ctx.owner.isClass) { @@ -516,7 +517,7 @@ class TreeUnpickler(reader: TastyReader, val rhsStart = currentAddr val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (givenFlags, annotFns, privateWithin) = readModifiers(end) + val (givenFlags, annotFns, privateWithin) = readModifiers(end, readAnnot, readWithin, NoSymbol) pickling.println(i"creating symbol $name at $start with flags $givenFlags") val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = @@ -550,7 +551,7 @@ class TreeUnpickler(reader: TastyReader, sym.completer.withDecls(newScope) forkAt(templateStart).indexTemplateParams()(localContext(sym)) } - else if (sym.isInlineableMethod) + else if (sym.isInlinedMethod) sym.addAnnotation(LazyBodyAnnotation { ctx0 => implicit val ctx: Context = localContext(sym)(ctx0).addMode(Mode.ReadPositions) // avoids space leaks by not capturing the current context @@ -563,10 +564,12 @@ class TreeUnpickler(reader: TastyReader, /** Read modifier list into triplet of flags, annotations and a privateWithin * boundary symbol. */ - def readModifiers(end: Addr)(implicit ctx: Context): (FlagSet, List[Symbol => Annotation], Symbol) = { + def readModifiers[WithinType, AnnotType] + (end: Addr, readAnnot: Context => Symbol => AnnotType, readWithin: Context => WithinType, defaultWithin: WithinType) + (implicit ctx: Context): (FlagSet, List[Symbol => AnnotType], WithinType) = { var flags: FlagSet = EmptyFlags - var annotFns: List[Symbol => Annotation] = Nil - var privateWithin: Symbol = NoSymbol + var annotFns: List[Symbol => AnnotType] = Nil + var privateWithin = defaultWithin while (currentAddr.index != end.index) { def addFlag(flag: FlagSet) = { flags |= flag @@ -612,10 +615,10 @@ class TreeUnpickler(reader: TastyReader, addFlag(ParamAccessor) case PRIVATEqualified => readByte() - privateWithin = readType().typeSymbol + privateWithin = readWithin(ctx) case PROTECTEDqualified => addFlag(Protected) - privateWithin = readType().typeSymbol + privateWithin = readWithin(ctx) case ANNOTATION => annotFns = readAnnot(ctx) :: annotFns case tag => @@ -625,6 +628,9 @@ class TreeUnpickler(reader: TastyReader, (flags, annotFns.reverse, privateWithin) } + private val readWithin: Context => Symbol = + implicit ctx => readType().typeSymbol + private val readAnnot: Context => Symbol => Annotation = { implicit ctx => readByte() @@ -633,9 +639,12 @@ class TreeUnpickler(reader: TastyReader, val lazyAnnotTree = readLaterWithOwner(end, rdr => ctx => rdr.readTerm()(ctx)) owner => - Annotation.deferredSymAndTree( - implicit ctx => tp.typeSymbol, - implicit ctx => lazyAnnotTree(owner).complete) + if (tp.isRef(defn.BodyAnnot)) + LazyBodyAnnotation(implicit ctx => lazyAnnotTree(owner).complete) + else + Annotation.deferredSymAndTree( + implicit ctx => tp.typeSymbol, + implicit ctx => lazyAnnotTree(owner).complete) } /** Create symbols for the definitions in the statement sequence between @@ -746,9 +755,9 @@ class TreeUnpickler(reader: TastyReader, ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(localCtx)), sym) def DefDef(tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree) = - ta.assignType( - untpd.DefDef(sym.name.asTermName, tparams, vparamss, tpt, readRhs(localCtx)), - sym) + ta.assignType( + untpd.DefDef(sym.name, tparams, vparamss, tpt, readRhs(localCtx)), + sym) def TypeDef(rhs: Tree) = ta.assignType(untpd.TypeDef(sym.name.asTypeName, rhs), sym) @@ -756,18 +765,23 @@ class TreeUnpickler(reader: TastyReader, def ta = ctx.typeAssigner val name = readName() + + def readDefDef() = { + val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) + val vparamss = readParamss(localCtx) + val tpt = readTpt()(localCtx) + val typeParams = tparams.map(_.symbol.asType) + val valueParamss = ctx.normalizeIfConstructor( + vparamss.nestedMap(_.symbol.asTerm), name == nme.CONSTRUCTOR) + val resType = ctx.effectiveResultType(sym, typeParams, tpt.tpe) + sym.info = ctx.lambdaType(typeParams, valueParamss, resType, sym) + DefDef(tparams, vparamss, tpt) + } + pickling.println(s"reading def of $name at $start") val tree: MemberDef = tag match { case DEFDEF => - val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) - val vparamss = readParamss(localCtx) - val tpt = readTpt()(localCtx) - val typeParams = tparams.map(_.symbol) - val valueParamss = ctx.normalizeIfConstructor( - vparamss.nestedMap(_.symbol), name == nme.CONSTRUCTOR) - val resType = ctx.effectiveResultType(sym, typeParams, tpt.tpe) - sym.info = ctx.methodType(typeParams, valueParamss, resType) - DefDef(tparams, vparamss, tpt) + readDefDef() case VALDEF => val tpt = readTpt()(localCtx) sym.info = tpt.tpe @@ -787,7 +801,10 @@ class TreeUnpickler(reader: TastyReader, else sym.registerCompanionMethod(nme.COMPANION_MODULE_METHOD, companion) } TypeDef(readTemplate(localCtx)) - } else { + } + else if (sym.is(Transparent)) + readDefDef() + else { sym.info = TypeBounds.empty // needed to avoid cyclic references when unpicklin rhs, see i3816.scala sym.setFlag(Provisional) val rhs = readTpt()(localCtx) @@ -871,7 +888,7 @@ class TreeUnpickler(reader: TastyReader, else EmptyValDef cls.info = ClassInfo(cls.owner.thisType, cls, parentTypes, cls.unforcedDecls, if (self.isEmpty) NoType else self.tpt.tpe) - cls.setNoInitsFlags(fork.indexStats(end)) + cls.setNoInitsFlags(parentsKind(parents), fork.indexStats(end)) val constr = readIndexedDef().asInstanceOf[DefDef] val mappedParents = parents.map(_.changeOwner(localDummy, constr.symbol)) @@ -924,26 +941,27 @@ class TreeUnpickler(reader: TastyReader, readByte() readEnd() val expr = readTerm() - def readSelectors(): List[untpd.Tree] = nextByte match { - case IMPORTED => - val start = currentAddr - readByte() - val from = setPos(start, untpd.Ident(readName())) - nextByte match { - case RENAMED => - val start2 = currentAddr - readByte() - val to = setPos(start2, untpd.Ident(readName())) - untpd.Thicket(from, to) :: readSelectors() - case _ => - from :: readSelectors() - } - case _ => - Nil - } setPos(start, Import(expr, readSelectors())) } + def readSelectors()(implicit ctx: Context): List[untpd.Tree] = nextByte match { + case IMPORTED => + val start = currentAddr + readByte() + val from = setPos(start, untpd.Ident(readName())) + nextByte match { + case RENAMED => + val start2 = currentAddr + readByte() + val to = setPos(start2, untpd.Ident(readName())) + untpd.Thicket(from, to) :: readSelectors() + case _ => + from :: readSelectors() + } + case _ => + Nil + } + def readIndexedStats(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[Tree] = until(end)(readIndexedStat(exprOwner)) @@ -1105,13 +1123,15 @@ class TreeUnpickler(reader: TastyReader, val parent = readTpt() val refinements = readStats(refineCls, end)(localContext(refineCls)) RefinedTypeTree(parent, refinements, refineCls) - case APPLIEDtpt => + case APPLYtpt => + tpd.Apply(readTpt(), until(end)(readTerm())) + case TYPEAPPLYtpt => // If we do directly a tpd.AppliedType tree we might get a // wrong number of arguments in some scenarios reading F-bounded // types. This came up in #137 of collection strawman. val tycon = readTpt() val args = until(end)(readTpt()) - untpd.AppliedTypeTree(tycon, args).withType(tycon.tpe.safeAppliedTo(args.tpes)) + untpd.TypeApply(tycon, args).withType(tycon.tpe.safeAppliedTo(args.tpes)) case ANDtpt => val tpt1 = readTpt() val tpt2 = readTpt() @@ -1130,7 +1150,9 @@ class TreeUnpickler(reader: TastyReader, val hi = if (currentAddr == end) lo else readTpt() TypeBoundsTree(lo, hi) case HOLE => - readHole(end, isType = false) + readHole(end) + case UNTYPEDSPLICE => + tpd.UntypedSplice(readUntyped()).withType(readType()) case _ => readPathTerm() } @@ -1184,7 +1206,7 @@ class TreeUnpickler(reader: TastyReader, owner => new LazyReader(localReader, owner, ctx.mode, op) } - def readHole(end: Addr, isType: Boolean)(implicit ctx: Context): Tree = { + def readHole(end: Addr)(implicit ctx: Context): Tree = { val idx = readNat() val args = until(end)(readTerm()) val splice = splices(idx) @@ -1192,15 +1214,214 @@ class TreeUnpickler(reader: TastyReader, if (arg.isTerm) new TastyTreeExpr(arg) else new TreeType(arg) val reifiedArgs = args.map(wrap) - if (isType) { - val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs) - PickledQuotes.quotedTypeToTree(quotedType) - } else { - val quotedExpr = splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](reifiedArgs) - PickledQuotes.quotedExprToTree(quotedExpr) + splice.asInstanceOf[Seq[Any] => Any](reifiedArgs) match { + case spliced: quoted.Type[_] => PickledQuotes.quotedTypeToTree(spliced) + case spliced: quoted.Expr[_] => PickledQuotes.quotedExprToTree(spliced) + } + } +// ------ Reading untyped trees -------------------------------------------- + + def readUntyped()(implicit ctx: Context): untpd.Tree = { + val start = currentAddr + val tag = readByte() + pickling.println(s"reading term ${astTagToString(tag)} at $start") + + def readDummyType(): Unit = { + assert(readByte() == SHAREDtype) + assert(readNat() == 0) + } + + def readIdent(): untpd.Ident = readUntyped().asInstanceOf[untpd.Ident] + + def readParams[T <: untpd.MemberDef](tag: Int): List[T] = + collectWhile(nextByte == tag) { + import untpd.modsDeco + val m: T = readUntyped().asInstanceOf[T] + m.withMods(m.mods | Param).asInstanceOf[T] + } + + def readParamss(): List[List[untpd.ValDef]] = + collectWhile(nextByte == PARAMS) { + readByte() + readEnd() + readParams[untpd.ValDef](PARAM) + } + + def readCases(end: Addr): List[untpd.CaseDef] = + collectWhile((nextUnsharedTag == CASEDEF) && currentAddr != end) { + readUntyped().asInstanceOf[untpd.CaseDef] + } + + def readSimpleTerm(): untpd.Tree = (tag: @switch) match { + case TERMREF => + val name = readName() + readDummyType() + untpd.Ident(name) + case TYPEREF => + val name = readName().toTypeName + readDummyType() + untpd.Ident(name) + case SELECT => + val name = readName() + val qual = readUntyped() + untpd.Select(qual, name) + case SELECTtpt => + val name = readName().toTypeName + val qual = readUntyped() + untpd.Select(qual, name) + case QUALTHIS => + untpd.This(readIdent()) + case NEW => + untpd.New(readUntyped()) + case THROW => + untpd.Throw(readUntyped()) + case SINGLETONtpt => + untpd.SingletonTypeTree(readUntyped()) + case BYNAMEtpt => + untpd.ByNameTypeTree(readUntyped()) + case NAMEDARG => + untpd.NamedArg(readName(), readUntyped()) + case EMPTYTREE => + untpd.EmptyTree + case SHAREDtype => + val b = readNat() + assert(b == 0, i"bad shared type $b at $currentAddr when reading ${ctx.owner.ownersIterator.toList}%, %") + untpd.TypeTree() + case _ => + untpd.Literal(readConstant(tag)) + } + + def readLengthTerm(): untpd.Tree = { + val end = readEnd() + + def readMods(): untpd.Modifiers = { + val (flags, annots, privateWithin) = + readModifiers(end, readUntypedAnnot, readUntypedWithin, EmptyTypeName) + untpd.Modifiers(flags, privateWithin, annots.map(_(NoSymbol))) + } + + def readRhs(): untpd.Tree = + if (nothingButMods(end)) untpd.EmptyTree else readUntyped() + + val result = (tag: @switch) match { + case SUPER => + val qual = readUntyped() + val mixId = ifBefore(end)(readIdent(), untpd.EmptyTypeIdent) + untpd.Super(qual, mixId) + case APPLY => + val fn = readUntyped() + untpd.Apply(fn, until(end)(readUntyped())) + case TYPEAPPLY => + untpd.TypeApply(readUntyped(), until(end)(readUntyped())) + case TYPED => + val expr = readUntyped() + val tpt = readUntyped() + untpd.Typed(expr, tpt) + case ASSIGN => + untpd.Assign(readUntyped(), readUntyped()) + case BLOCK => + val expr = readUntyped() + val stats = until(end)(readUntyped()) + untpd.Block(stats, expr) + case IF => + untpd.If(readUntyped(), readUntyped(), readUntyped()) + case MATCH => + untpd.Match(readUntyped(), readCases(end)) + case CASEDEF => + val pat = readUntyped() + val rhs = readUntyped() + val guard = ifBefore(end)(readUntyped(), untpd.EmptyTree) + untpd.CaseDef(pat, guard, rhs) + case RETURN => + readNat() + val expr = ifBefore(end)(readUntyped(), untpd.EmptyTree) + untpd.Return(expr, untpd.EmptyTree) + case TRY => + untpd.Try(readUntyped(), readCases(end), ifBefore(end)(readUntyped(), untpd.EmptyTree)) + case BIND => + val name = readName() + readDummyType() + untpd.Bind(name, readUntyped()) + case ALTERNATIVE => + untpd.Alternative(until(end)(readUntyped())) + case DEFDEF => + var name = readName() + val tparams = readParams[TypeDef](TYPEPARAM) + val vparamss = readParamss() + val tpt = readUntyped() + untpd.DefDef( + if (untpd.isBounds(tpt)) name.toTypeName else name, + tparams, vparamss, tpt, readRhs()).withMods(readMods()) + case VALDEF | PARAM => + untpd.ValDef(readName(), readUntyped(), readRhs()) + .withMods(readMods()) + case TYPEDEF | TYPEPARAM => + untpd.TypeDef(readName().toTypeName, readUntyped()) + .withMods(readMods()) + case OBJECTDEF => + untpd.ModuleDef(readName(), readUntyped().asInstanceOf[untpd.Template]) + .withMods(readMods()) + case TEMPLATE => + val parents = collectWhile(nextByte != SELFDEF && nextByte != DEFDEF)(readUntyped()) + val self = + if (nextByte == SELFDEF) { + readByte() + untpd.ValDef(readName(), readUntyped(), untpd.EmptyTree) + } + else untpd.EmptyValDef + val constr = readUntyped().asInstanceOf[untpd.DefDef] + val body = until(end)(readUntyped()) + untpd.Template(constr, parents, self, body) + case IMPORT => + untpd.Import(readUntyped(), readSelectors()) + case REFINEDtpt => + untpd.RefinedTypeTree(readUntyped(), until(end)(readUntyped())) + case ANDtpt => + untpd.AndTypeTree(readUntyped(), readUntyped()) + case ORtpt => + untpd.OrTypeTree(readUntyped(), readUntyped()) + case ANNOTATEDtpt => + untpd.Annotated(readUntyped(), readUntyped()) + case LAMBDAtpt => + val tparams = readParams[TypeDef](TYPEPARAM) + val body = readUntyped() + untpd.LambdaTypeTree(tparams, body) + case TYPEBOUNDStpt => + val lo = readUntyped() + val hi = ifBefore(end)(lo, readUntyped()) + untpd.TypeBoundsTree(lo, hi) + case TYPEDSPLICE => + untpd.TypedSplice(readTerm()) + case FUNCTION => + val body = readUntyped() + import untpd.modsDeco + val params = until(end)(readUntyped()).map { + case param: untpd.ValDef => param.withMods(param.mods | Param) + case param => param + } + untpd.Function(params, body) + case INFIXOP => + untpd.InfixOp(readUntyped(), readIdent(), readUntyped()) + case PATDEF => + val tpt = readUntyped() + val rhs = readUntyped() + val pats = collectWhile(!nothingButMods(end))(readUntyped()) + untpd.PatDef(readMods(), pats, tpt, rhs) + } + assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") + result } + + val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() + setPos(start, tree) } + private val readUntypedWithin: Context => TypeName = + implicit ctx => readName().toTypeName + + private val readUntypedAnnot: Context => Symbol => untpd.Tree = + implicit ctx => _ => readUntyped() + // ------ Setting positions ------------------------------------------------ /** Pickled position for `addr`. */ diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f7dbda218e0e..3bd852e93586 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -1210,7 +1210,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case APPLIEDTYPEtree => val tpt = readTreeRef() val args = until(end, () => readTreeRef()) - AppliedTypeTree(tpt, args) + TypeApply(tpt, args) case TYPEBOUNDStree => val lo = readTreeRef() diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 5c203d123f4a..0007c32da6da 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -105,7 +105,7 @@ object JavaParsers { def javaLangObject(): Tree = javaLangDot(tpnme.Object) def arrayOf(tpt: Tree) = - AppliedTypeTree(Ident(nme.Array.toTypeName), List(tpt)) + TypeApply(Ident(nme.Array.toTypeName), List(tpt)) def unimplementedExpr(implicit ctx: Context) = Select(Select(rootDot(nme.scala_), nme.Predef), nme.???) @@ -230,7 +230,7 @@ object JavaParsers { def convertToTypeId(tree: Tree): Tree = convertToTypeName(tree) match { case Some(t) => t withPos tree.pos case _ => tree match { - case AppliedTypeTree(_, _) | Select(_, _) => + case TypeApply(_, _) | Select(_, _) => tree case _ => syntaxError(IdentifierExpected(tree.show), tree.pos) @@ -327,7 +327,7 @@ object JavaParsers { val args = repsep(() => typeArg(), COMMA) acceptClosingAngle() atPos(t1.pos.start) { - AppliedTypeTree(t1, args) + TypeApply(t1, args) } } else t } @@ -784,7 +784,7 @@ object JavaParsers { // If the annotation has a default value we don't need to parse it, providing // any value at all is enough to typecheck usages of annotations correctly. val defaultParam = if (hasDefault) unimplementedExpr else EmptyTree - makeParam(dd.name, dd.tpt, defaultParam) + makeParam(dd.name.asTermName, dd.tpt, defaultParam) } val constr = DefDef(nme.CONSTRUCTOR, List(), List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined)) @@ -835,7 +835,7 @@ object JavaParsers { accept(RBRACE) /* val superclazz = - AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) + TypeApply(javaLangDot(tpnme.Enum), List(enumType)) */ val superclazz = Apply(TypeApply( Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9ffa8cf9c52e..18f53797ac35 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -46,7 +46,7 @@ object Parsers { } @sharable object ParamOwner extends Enumeration { - val Class, Type, TypeParam, Def = Value + val Class, Type, TypeParam, Def, Constructor = Value } private implicit class AddDeco(val buf: ListBuffer[Tree]) extends AnyVal { @@ -714,6 +714,7 @@ object Parsers { } /* ------------- TYPES ------------------------------------------------------ */ + /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and * returns a tree for type `Any` instead. */ @@ -783,7 +784,8 @@ object Parsers { refinedTypeRest( withTypeRest( annotTypeRest( - simpleTypeRest(tuple))))) + appliedTypeRest( + simpleTypeRest(tuple)))))) } } } @@ -846,14 +848,20 @@ object Parsers { } else t - /** AnnotType ::= SimpleType {Annotation} + /** AnnotType ::= AppliedType {Annotation} */ - def annotType(): Tree = annotTypeRest(simpleType()) + def annotType(): Tree = annotTypeRest(appliedType()) def annotTypeRest(t: Tree): Tree = if (in.token == AT) annotTypeRest(atPos(startOffset(t)) { Annotated(t, annot()) }) else t + /** AppiedType ::= SimpleType {ParArgumentExprs} + */ + def appliedType(): Tree = appliedTypeRest(simpleType()) + + def appliedTypeRest(t: Tree) = parArgumentExprss(t) + /** SimpleType ::= SimpleType TypeArgs * | SimpleType `#' id * | StableId @@ -893,7 +901,7 @@ object Parsers { private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) case LBRACKET => simpleTypeRest(atPos(startOffset(t)) { - AppliedTypeTree(checkWildcard(t), typeArgs(namedOK = false, wildOK = true)) }) + TypeApply(checkWildcard(t), typeArgs(namedOK = false, wildOK = true)) }) case _ => t } @@ -988,7 +996,7 @@ object Parsers { def contextBounds(pname: TypeName): List[Tree] = in.token match { case COLON => atPos(in.skipToken()) { - AppliedTypeTree(toplevelTyp(), Ident(pname)) + TypeApply(toplevelTyp(), Ident(pname) :: Nil) } :: contextBounds(pname) case VIEWBOUND => deprecationWarning("view bounds `<%' are deprecated, use a context bound `:' instead") @@ -1048,6 +1056,21 @@ object Parsers { case None => t } + /** TypeRHS ::= ‘if’ Expr ‘then’ TypeRHS ‘else’ TypeRHS + * | Type + */ + def typeRHS(): Tree = + if (in.token == IF) + atPos(in.skipToken()) { + val cond = expr() + accept(THEN) + val thenp = typeRHS() + accept(ELSE) + val elsep = typeRHS() + If(cond, thenp, elsep) + } + else toplevelTyp() + /* ----------- EXPRESSIONS ------------------------------------------------ */ /** EqualsExpr ::= `=' Expr @@ -1824,14 +1847,15 @@ object Parsers { */ def typeParamClause(ownerKind: ParamOwner.Value): List[TypeDef] = inBrackets { def typeParam(): TypeDef = { - val isConcreteOwner = ownerKind == ParamOwner.Class || ownerKind == ParamOwner.Def + val isDefOwner = ownerKind == ParamOwner.Def || ownerKind == ParamOwner.Constructor + val isConcreteOwner = isDefOwner || ownerKind == ParamOwner.Class val start = in.offset val mods = atPos(start) { annotsAsMods() | { if (ownerKind == ParamOwner.Class) Param | PrivateLocal else Param } | { - if (ownerKind != ParamOwner.Def) + if (!isDefOwner) if (isIdent(nme.raw.PLUS)) { in.nextToken(); Covariant } else if (isIdent(nme.raw.MINUS)) { in.nextToken(); Contravariant } else EmptyFlags @@ -1868,14 +1892,14 @@ object Parsers { * DefParam ::= {Annotation} [`inline'] Param * Param ::= id `:' ParamType [`=' Expr] */ - def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = { + def paramClauses(ownerKind: ParamOwner.Value, ofCaseClass: Boolean = false): List[List[ValDef]] = { var imods: Modifiers = EmptyModifiers var implicitOffset = -1 // use once var firstClauseOfCaseClass = ofCaseClass def param(): ValDef = { val start = in.offset var mods = annotsAsMods() - if (owner.isTypeName) { + if (ownerKind == ParamOwner.Class) { mods = addFlag(modifiers(start = mods), ParamAccessor) mods = atPos(start, in.offset) { @@ -1902,7 +1926,7 @@ object Parsers { atPos(start, nameStart) { val name = ident() accept(COLON) - if (in.token == ARROW && owner.isTypeName && !(mods is Local)) + if (in.token == ARROW && ownerKind == ParamOwner.Class && !(mods is Local)) syntaxError(VarValParametersMayNotBeCallByName(name, mods is Mutable)) val tpt = paramType() val default = @@ -1929,7 +1953,7 @@ object Parsers { funArgMods() } } - funArgMods() + if (ownerKind != ParamOwner.Type) funArgMods() commaSeparated(() => param()) } @@ -1946,7 +1970,7 @@ object Parsers { } val start = in.offset val result = clauses() - if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods is Implicit)))) { + if (ownerKind == ParamOwner.Constructor && (result.isEmpty || (result.head take 1 exists (_.mods is Implicit)))) { in.token match { case LBRACKET => syntaxError("no type parameters allowed here") case EOF => incompleteInputError(AuxConstructorNeedsNonImplicitParameter()) @@ -2114,7 +2138,7 @@ object Parsers { } if (in.token == THIS) { in.nextToken() - val vparamss = paramClauses(nme.CONSTRUCTOR) + val vparamss = paramClauses(ParamOwner.Constructor) if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE) val rhs = { if (!(in.token == LBRACE && scala2ProcedureSyntax(""))) accept(EQUALS) @@ -2125,7 +2149,7 @@ object Parsers { val mods1 = addFlag(mods, Method) val name = ident() val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses(name) + val vparamss = paramClauses(ParamOwner.Def) var tpt = fromWithinReturnType(typedOpt()) if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE) val rhs = @@ -2176,24 +2200,32 @@ object Parsers { Block(stats, Literal(Constant(()))) } - /** TypeDef ::= type id [TypeParamClause] `=' Type - * TypeDcl ::= type id [TypeParamClause] TypeBounds + /** TypeDcl ::= id [TypTypeParamClause] [DefParamClause] TypeBounds ‘=’ TypeRHS + * | id [HkTypeParamClause] TypeBounds */ def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { newLinesOpt() atPos(start, nameStart) { val name = ident().toTypeName val tparams = typeParamClauseOpt(ParamOwner.Type) - in.token match { - case EQUALS => + val vparamss = paramClauses(ParamOwner.Type) + if (vparamss.length > 1) syntaxError(i"type definitions cannot have multiple parameter clauses") + val isBounded = in.token == SUPERTYPE || in.token == SUBTYPE + val bounds = typeBounds() + val res = + if (in.token == EQUALS) { in.nextToken() - TypeDef(name, lambdaAbstract(tparams, toplevelTyp())).withMods(mods).setComment(in.getDocComment(start)) - case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF => - TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(in.getDocComment(start)) - case _ => - syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token)) - EmptyTree - } + val rhs = typeRHS() + if (isTypeDefRHS(rhs) || isBounded || vparamss.nonEmpty) + DefDef(name, tparams, vparamss, bounds, rhs) + else + TypeDef(name, lambdaAbstract(tparams, rhs)) + } + else { + if (vparamss.nonEmpty) syntaxError("`=' expected") + TypeDef(name, lambdaAbstract(tparams, bounds)) + } + res.withMods(mods).setComment(in.getDocComment(start)) } } @@ -2238,7 +2270,7 @@ object Parsers { def classConstr(owner: Name, isCaseClass: Boolean = false): DefDef = atPos(in.lastOffset) { val tparams = typeParamClauseOpt(ParamOwner.Class) val cmods = fromWithinClassConstr(constrModsOpt(owner)) - val vparamss = paramClauses(owner, isCaseClass) + val vparamss = paramClauses(ParamOwner.Class, isCaseClass) makeConstructor(tparams, vparamss).withMods(cmods) } @@ -2312,11 +2344,11 @@ object Parsers { /* -------- TEMPLATES ------------------------------------------- */ - /** ConstrApp ::= SimpleType {ParArgumentExprs} + /** ConstrApp ::= SimpleType {Annotation} {ParArgumentExprs} */ val constrApp = () => { // Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code - val t = checkWildcard(annotType(), fallbackTree = Ident(nme.ERROR)) + val t = checkWildcard(annotTypeRest(simpleType()), fallbackTree = Ident(nme.ERROR)) if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) else t } diff --git a/compiler/src/dotty/tools/dotc/parsing/ScriptParsers.scala b/compiler/src/dotty/tools/dotc/parsing/ScriptParsers.scala index afa7fefab860..a8923fe2bdb3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/ScriptParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/ScriptParsers.scala @@ -127,7 +127,7 @@ object ScriptParsers { ) // def main - def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) + def mainParamType = TypeApply(Ident(tpnme.Array), List(Ident(tpnme.String))) def mainParameter = List(ValDef(Modifiers(Param), "argv", mainParamType, EmptyTree)) def mainSetArgv = List(ValDef(Modifiers(), "args", TypeTree(), Ident("argv"))) def mainNew = makeNew(Nil, emptyValDef, stmts, List(Nil), NoPosition, NoPosition) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 79c00e0f9f92..74da6bb04367 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -167,12 +167,13 @@ class PlainPrinter(_ctx: Context) extends Printer { "" case NoPrefix => "" - case tp: MethodType => + case tp: TermLambda => changePrec(GlobalPrec) { ("(" + (if (tp.isErasedMethod) "erased " else "") + (if (tp.isImplicitMethod) "implicit " else "") ) ~ paramsText(tp) ~ - (if (tp.resultType.isInstanceOf[MethodType]) ")" else "): ") ~ + (if (tp.isInstanceOf[HKTermLambda] || + tp.resultType.isInstanceOf[MethodType]) ")" else "): ") ~ toText(tp.resultType) } case tp: ExprType => @@ -326,7 +327,7 @@ class PlainPrinter(_ctx: Context) extends Printer { else dclsText(trueDecls) tparamsText ~ " extends " ~ toTextParents(tp.parents) ~ "{" ~ selfText ~ declsText ~ "} at " ~ preText - case mt: MethodType => + case mt: LambdaType => toTextGlobal(mt) case tp: ExprType => ": => " ~ toTextGlobal(tp.widenExpr) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f1cf87ad9c1f..a11322abbaf9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -208,12 +208,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ": " ~ toText(tp.memberProto) ~ " }" case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) - case tp @ FunProto(args, resultType, _) => - val argsText = args match { + case tp: FunProto => + val argsText = tp.args match { case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp) - case _ => toTextGlobal(args, ", ") + case _ => toTextGlobal(tp.args, ", ") } - return "FunProto(" ~ argsText ~ "):" ~ toText(resultType) + return "FunProto(" ~ argsText ~ "):" ~ toText(tp.resultType) case tp: IgnoredProto => return "?" case tp @ PolyProto(targs, resType) => @@ -261,7 +261,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(GlobalPrec) { keywordStr("for ") ~ Text(enums map enumText, "; ") ~ sep ~ toText(expr) } def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD - case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt) + case TypeApply(tpt, _) => " : " ~ toText(tpt) case untpd.Function(_, tpt) => " <% " ~ toText(tpt) } @@ -379,8 +379,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(OrPrec) { toText(l) ~ " | " ~ toText(r) } case RefinedTypeTree(tpt, refines) => toTextLocal(tpt) ~ " " ~ blockText(refines) - case AppliedTypeTree(tpt, args) => - toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]" case LambdaTypeTree(tparams, body) => changePrec(GlobalPrec) { tparamsText(tparams) ~ " -> " ~ toText(body) @@ -446,6 +444,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case TypedSplice(t) => if (ctx.settings.YprintDebug.value) "[" ~ toText(t) ~ "]#TS#" else toText(t) + case tpd.UntypedSplice(t) => + if (ctx.settings.YprintDebug.value) "[" ~ toText(t) ~ ":" ~ toText(tree.typeOpt) ~ "]#US#" + else toText(t) case tree @ ModuleDef(name, impl) => withEnclosingDef(tree) { modText(tree.mods, NoSymbol, keywordStr("object")) ~~ nameIdText(tree) ~ toTextTemplate(impl) @@ -590,7 +591,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY, sym.annotations map (_.tree)) - protected def optAscription[T >: Untyped](tpt: Tree[T]) = optText(tpt)(": " ~ _) + protected def optAscription[T >: Untyped](tpt: Tree[T])(implicit ctx: Context) = + if (tpt.typeOpt.isInstanceOf[TypeBounds]) toText(tpt) + else optText(tpt)(": " ~ _) private def idText(tree: untpd.Tree): Text = { if ((ctx.settings.uniqid.value || Printer.debugPrintUnique) && tree.hasType && tree.symbol.exists) s"#${tree.symbol.id}" else "" @@ -638,7 +641,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { protected def defDefToText[T >: Untyped](tree: DefDef[T]): Text = { import untpd.{modsDeco => _, _} dclTextOr(tree) { - val prefix = modText(tree.mods, tree.symbol, keywordStr("def")) ~~ valDefText(nameIdText(tree)) + val keyword = if (tree.symbol.isType) "type" else "def" + val prefix = modText(tree.mods, tree.symbol, keywordStr(keyword)) ~~ valDefText(nameIdText(tree)) withEnclosingDef(tree) { addVparamssText(prefix ~ tparamsText(tree.tparams), tree.vparamss) ~ optAscription(tree.tpt) ~ optText(tree.rhs)(" = " ~ _) diff --git a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala index 4adf5405590a..a06b14060fb5 100644 --- a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala @@ -23,7 +23,8 @@ class StoreReporter(outer: Reporter) extends Reporter { protected[this] var infos: mutable.ListBuffer[MessageContainer] = null def doReport(m: MessageContainer)(implicit ctx: Context): Unit = { - typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG + typr.println(s">>>> StoredError: ${m.message}") + // new AssertionError().printStackTrace() // DEBUG if (infos == null) infos = new mutable.ListBuffer infos += m } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 79f5c33d8779..3457009baf30 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1260,7 +1260,7 @@ object messages { |""".stripMargin } - case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context) + case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.Name)(implicit ctx: Context) extends Message(OverloadedOrRecursiveMethodNeedsResultTypeID) { val kind = "Syntax" val msg = hl"""overloaded or recursive method ${tree} needs return type""" diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index e26fe556510b..4dcd282852c3 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -328,7 +328,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } else if (sym.is(Mutable, butNot = Accessor)) { api.Var.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray, apiType(sym.info)) - } else if (sym.isStable) { + } else if (sym.isStable && !sym.isRealMethod) { api.Val.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray, apiType(sym.info)) } else { diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala index fdd78ccc4ed7..6dd63b263e91 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala @@ -669,7 +669,7 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s object Applied extends AppliedExtractor { def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, List[TypeOrBoundsTree])] = x match { - case x: tpd.AppliedTypeTree @unchecked => Some(x.tpt, x.args) + case x: tpd.TypeApply @unchecked => Some(x.fun, x.args) case _ => None } } @@ -759,12 +759,14 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s type LambdaType[ParamInfo <: TypeOrBounds] = Types.LambdaType { type PInfo = ParamInfo } type MethodType = Types.MethodType type PolyType = Types.PolyType + type TermLambda = Types.TermLambda type TypeLambda = Types.TypeLambda def typeClassTag: ClassTag[Type] = implicitly[ClassTag[Type]] def recursiveTypeClassTag: ClassTag[RecursiveType] = implicitly[ClassTag[RecursiveType]] def methodTypeClassTag: ClassTag[MethodType] = implicitly[ClassTag[MethodType]] def polyTypeClassTag: ClassTag[PolyType] = implicitly[ClassTag[PolyType]] + def termLambdaClassTag: ClassTag[TermLambda] = implicitly[ClassTag[TermLambda]] def typeLambdaClassTag: ClassTag[TypeLambda] = implicitly[ClassTag[TypeLambda]] def MethodTypeDeco(tpe: MethodType): MethodTypeAPI = new MethodTypeAPI { @@ -781,6 +783,12 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s def resultTpe(implicit ctx: Context): Type = tpe.resType } + def TermLambdaDeco(tpe: Types.TermLambda): TermLambdaAPI = new TermLambdaAPI { + def paramNames(implicit ctx: Context): List[String] = tpe.paramNames.map(_.toString) + def paramTypes(implicit ctx: Context): List[Type] = tpe.paramInfos + def resultTpe(implicit ctx: Context): Type = tpe.resType + } + def TypeLambdaDeco(tpe: Types.TypeLambda): TypeLambdaAPI = new TypeLambdaAPI { def paramNames(implicit ctx: Context): List[String] = tpe.paramNames.map(_.toString) def paramTypes(implicit ctx: Context): List[TypeBounds] = tpe.paramInfos @@ -928,6 +936,13 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s } } + object TermLambda extends TermLambdaExtractor { + def unapply(x: TermLambda)(implicit ctx: Context): Option[(List[String], List[Type], Type)] = x match { + case x: TermLambda => Some(x.paramNames.map(_.toString), x.paramInfos, x.resType) + case _ => None + } + } + object TypeLambda extends TypeLambdaExtractor { def unapply(x: TypeLambda)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] = x match { case x: TypeLambda => Some(x.paramNames.map(_.toString), x.paramInfos, x.resType) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index d401ce5aa3c4..38bdddb94b38 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -540,35 +540,37 @@ object Erasure { * with more than `MaxImplementedFunctionArity` parameters to ise a single * parameter of type `[]Object`. */ - override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { - val restpe = - if (sym.isConstructor) defn.UnitType - else sym.info.resultType - var vparamss1 = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil - var rhs1 = ddef.rhs match { - case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) - case _ => ddef.rhs - } - if (sym.isAnonymousFunction && vparamss1.head.length > MaxImplementedFunctionArity) { - val bunchedParam = ctx.newSymbol(sym, nme.ALLARGS, Flags.TermParam, JavaArrayType(defn.ObjectType)) - def selector(n: Int) = ref(bunchedParam) - .select(defn.Array_apply) - .appliedTo(Literal(Constant(n))) - val paramDefs = vparamss1.head.zipWithIndex.map { - case (paramDef, idx) => - assignType(untpd.cpy.ValDef(paramDef)(rhs = selector(idx)), paramDef.symbol) + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + if (sym.isType) EmptyTree + else { + val restpe = + if (sym.isConstructor) defn.UnitType + else sym.info.resultType + var vparamss1 = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil + var rhs1 = ddef.rhs match { + case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe)) + case _ => ddef.rhs } - vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil - rhs1 = untpd.Block(paramDefs, rhs1) + if (sym.isAnonymousFunction && vparamss1.head.length > MaxImplementedFunctionArity) { + val bunchedParam = ctx.newSymbol(sym, nme.ALLARGS, Flags.TermParam, JavaArrayType(defn.ObjectType)) + def selector(n: Int) = ref(bunchedParam) + .select(defn.Array_apply) + .appliedTo(Literal(Constant(n))) + val paramDefs = vparamss1.head.zipWithIndex.map { + case (paramDef, idx) => + assignType(untpd.cpy.ValDef(paramDef)(rhs = selector(idx)), paramDef.symbol) + } + vparamss1 = (tpd.ValDef(bunchedParam) :: Nil) :: Nil + rhs1 = untpd.Block(paramDefs, rhs1) + } + vparamss1 = vparamss1.mapConserve(_.filterConserve(!_.symbol.is(Flags.Erased))) + val ddef1 = untpd.cpy.DefDef(ddef)( + tparams = Nil, + vparamss = vparamss1, + tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), + rhs = rhs1) + super.typedDefDef(ddef1, sym) } - vparamss1 = vparamss1.mapConserve(_.filterConserve(!_.symbol.is(Flags.Erased))) - val ddef1 = untpd.cpy.DefDef(ddef)( - tparams = Nil, - vparamss = vparamss1, - tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)), - rhs = rhs1) - super.typedDefDef(ddef1, sym) - } override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = { val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol) diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 92230e38310b..9902c7241162 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,10 +153,10 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => if (tree.isType) toTypeTree(tree) else constToLiteral(tree) override def transformTypeApply(tree: TypeApply)(implicit ctx: Context) = - constToLiteral(tree) + if (tree.isType) toTypeTree(tree) else constToLiteral(tree) override def transformApply(tree: Apply)(implicit ctx: Context) = - constToLiteral(foldCondition(tree)) + if (tree.isType) toTypeTree(tree) else constToLiteral(foldCondition(tree)) override def transformTyped(tree: Typed)(implicit ctx: Context) = constToLiteral(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/LinkAll.scala b/compiler/src/dotty/tools/dotc/transform/LinkAll.scala index 5fa9436f297a..153bc0815a92 100644 --- a/compiler/src/dotty/tools/dotc/transform/LinkAll.scala +++ b/compiler/src/dotty/tools/dotc/transform/LinkAll.scala @@ -44,7 +44,7 @@ class LinkAll extends Phase { private[this] var inParents = false override def apply(acc: Set[ClassDenotation], tree: tpd.Tree)(implicit ctx: Context): Set[ClassDenotation] = tree match { case New(tpt) => accum(acc, tpt.tpe.classSymbol) - case AppliedTypeTree(tpt, _) if inParents => accum(acc, tpt.symbol) + case TypeApply(tpt, _) if tpt.isType && inParents => accum(acc, tpt.symbol) case tree: RefTree if inParents || tree.symbol.is(Module) => foldOver(accum(acc, tree.symbol), tree) case tree @ Template(constr, parents, self, _) => diff --git a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala index 5c3dea9cb79e..8d561f5c916a 100644 --- a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -59,6 +59,8 @@ abstract class MacroTransform extends Phase { transform(parents)(ctx.superCallContext), transformSelf(self), transformStats(impl.body, tree.symbol)) + case UntypedSplice(_) => + tree case _ => super.transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 918d85afeca6..742b99b2b450 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -30,7 +30,7 @@ object PostTyper { * * (5) Convert all trees representing types to TypeTrees. * - * (6) Check the bounds of AppliedTypeTrees + * (6) Check the bounds of TypeApply type trees * * (7) Insert `.package` for selections of package object members * @@ -203,18 +203,25 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase super.transform(tree) } case tree: TypeApply => - val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) - if (fn.symbol != defn.ChildAnnot.primaryConstructor) { - // Make an exception for ChildAnnot, which should really have AnyKind bounds - Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) + // TODO simplify the following + if (tree.isType) { + Checking.checkAppliedType(tree, boundsCheck = !ctx.mode.is(Mode.Pattern)) + super.transform(tree) } - fn match { - case sel: Select => - val args1 = transform(args) - val sel1 = transformSelect(sel, args1) - cpy.TypeApply(tree1)(sel1, args1) - case _ => - super.transform(tree1) + else { + val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) + if (fn.symbol != defn.ChildAnnot.primaryConstructor) { + // Make an exception for ChildAnnot, which should really have AnyKind bounds + Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) + } + fn match { + case sel: Select => + val args1 = transform(args) + val sel1 = transformSelect(sel, args1) + cpy.TypeApply(tree1)(sel1, args1) + case _ => + super.transform(tree1) + } } case Inlined(call, bindings, expansion) => // Leave only a call trace consisting of @@ -264,9 +271,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase super.transform(tree) case tree @ Annotated(annotated, annot) => cpy.Annotated(tree)(transform(annotated), transformAnnot(annot)) - case tree: AppliedTypeTree => - Checking.checkAppliedType(tree, boundsCheck = !ctx.mode.is(Mode.Pattern)) - super.transform(tree) case SingletonTypeTree(ref) => Checking.checkRealizable(ref.tpe, ref.pos.focus) super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index 940ce9199a08..6fdb531758e9 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -80,13 +80,13 @@ class ResolveSuper extends MiniPhase with IdentityDenotTransformer { thisPhase = } override def transformDefDef(ddef: DefDef)(implicit ctx: Context) = { - val meth = ddef.symbol.asTerm + val meth = ddef.symbol if (meth.isSuperAccessor && !meth.is(Deferred)) { assert(ddef.rhs.isEmpty) val cls = meth.owner.asClass val ops = new MixinOps(cls, thisPhase) import ops._ - polyDefDef(meth, forwarder(rebindSuper(cls, meth))) + polyDefDef(meth.asTerm, forwarder(rebindSuper(cls, meth))) } else ddef } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index e1c36b9395c3..a0afefaa9d6a 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -249,7 +249,6 @@ class TreeChecker extends Phase with SymTransformer { // case tree: AndTypeTree => // case tree: OrTypeTree => // case tree: RefinedTypeTree => - // case tree: AppliedTypeTree => // case tree: ByNameTypeTree => // case tree: TypeBoundsTree => // case tree: Alternative => @@ -300,10 +299,11 @@ class TreeChecker extends Phase with SymTransformer { tree } - /** Check that all methods have MethodicType */ - def isMethodType(pt: Type)(implicit ctx: Context): Boolean = pt match { - case at: AnnotatedType => isMethodType(at.parent) - case _: MethodicType => true // MethodType, ExprType, PolyType + /** Check that all methods have LambdaType or ExprType */ + def isDefType(tp: Type)(implicit ctx: Context): Boolean = tp match { + case _: LambdaType | _: ExprType => true + case tp: TypeBounds => isDefType(tp.hi) + case tp: AnnotatedType => isDefType(tp.parent) case _ => false } @@ -406,7 +406,7 @@ class TreeChecker extends Phase with SymTransformer { }) val tpdTree = super.typedDefDef(ddef, sym) - assert(isMethodType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") + assert(isDefType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") tpdTree } } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 11ed46942e08..a2cc3e94ebcf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -143,7 +143,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * @param args the arguments of the application * @param resultType the expected result type of the application */ - abstract class Application[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) { + abstract class Application[Arg](methRef: NamedType, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) { /** The type of typed arguments: either tpd.Tree or Type */ type TypedArg @@ -211,15 +211,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** The function's type after widening and instantiating polytypes * with TypeParamRefs in constraint set */ - lazy val methType: Type = liftedFunType.widen match { - case funType: MethodType => funType - case funType: PolyType => constrained(funType).resultType + lazy val methType: Type = liftedFunType.widen.toLambda match { + case funType: TermLambda => funType + case funType: TypeLambda => constrained(funType).resultType case tp => tp //was: funType } def reqiredArgNum(tp: Type): Int = tp.widen match { - case funType: MethodType => funType.paramInfos.size - case funType: PolyType => reqiredArgNum(funType.resultType) + case funType: TermLambda => funType.paramInfos.size + case funType: TypeLambda => reqiredArgNum(funType.resultType) case tp => args.size } @@ -240,10 +240,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic => args protected def init() = methType match { - case methType: MethodType => + case methType: TermLambda => // apply the result type constraint, unless method type is dependent val resultApprox = resultTypeApprox(methType) - if (!constrainResult(resultApprox, resultType)) + if (!constrainResult(methRef.symbol, resultApprox, resultType)) if (ctx.typerState.isCommittable) // defer the problem until after the application; // it might be healed by an implicit conversion @@ -261,7 +261,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** The application was successful */ def success = ok - protected def methodType = methType.asInstanceOf[MethodType] + protected def lambda = methType.asInstanceOf[TermLambda] private def methString: String = i"${methRef.symbol}: ${methType.show}" /** Re-order arguments to correctly align named arguments */ @@ -292,7 +292,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop) else { // name not (or no longer) available for named arg def msg = - if (methodType.paramNames contains aname) + if (lambda.paramNames contains aname) s"parameter $aname of $methString is already instantiated" else s"$methString does not have a parameter $aname" @@ -317,7 +317,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case Nil => Nil } - handlePositional(methodType.paramNames, args) + handlePositional(lambda.paramNames, args) } /** Splice new method reference into existing application */ @@ -432,13 +432,13 @@ trait Applications extends Compatibility { self: Typer with Dynamic => */ def addTyped(arg: Arg, formal: Type): Type => Type = { addArg(typedArg(arg, formal), formal) - if (methodType.isParamDependent) - safeSubstParam(_, methodType.paramRefs(n), typeOfArg(arg)) + if (lambda.isParamDependent) + safeSubstParam(_, lambda.paramRefs(n), typeOfArg(arg)) else identity } def missingArg(n: Int): Unit = { - val pname = methodType.paramNames(n) + val pname = lambda.paramNames(n) fail( if (pname.firstPart contains '$') s"not enough arguments for $methString" else s"missing argument for parameter $pname of $methString") @@ -488,7 +488,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * in a "can/cannot apply" answer, without needing to construct trees or * issue error messages. */ - abstract class TestApplication[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) + abstract class TestApplication[Arg](methRef: NamedType, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) extends Application[Arg](methRef, funType, args, resultType) { type TypedArg = Arg type Result = Unit @@ -524,7 +524,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Subclass of Application for applicability tests with type arguments and value * argument trees. */ - class ApplicableToTrees(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) + class ApplicableToTrees(methRef: NamedType, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends TestApplication(methRef, methRef.widen.appliedTo(targs), args, resultType) { def argType(arg: Tree, formal: Type): Type = normalize(arg.tpe, formal) def treeToArg(arg: Tree): Tree = arg @@ -536,12 +536,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => /** Subclass of Application for applicability tests with type arguments and value * argument trees. */ - class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) { + class ApplicableToTreesDirectly(methRef: NamedType, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) { override def argOK(arg: TypedArg, formal: Type) = argType(arg, formal) <:< formal } /** Subclass of Application for applicability tests with value argument types. */ - class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context) + class ApplicableToTypes(methRef: NamedType, args: List[Type], resultType: Type)(implicit ctx: Context) extends TestApplication(methRef, methRef, args, resultType) { def argType(arg: Type, formal: Type): Type = arg def treeToArg(arg: Tree): Type = arg.tpe @@ -554,7 +554,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * types of arguments are either known or unknown. */ abstract class TypedApply[T >: Untyped]( - app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Trees.Tree[T]], resultType: Type)(implicit ctx: Context) + app: untpd.Apply, fun: Tree, methRef: NamedType, args: List[Trees.Tree[T]], resultType: Type)(implicit ctx: Context) extends Application(methRef, fun.tpe, args, resultType) { type TypedArg = Tree def isVarArg(arg: Trees.Tree[T]): Boolean = untpd.isWildcardStarArg(arg) @@ -658,7 +658,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } /** Subclass of Application for type checking an Apply node with untyped arguments. */ - class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, proto: FunProto, resultType: Type)(implicit ctx: Context) + class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: NamedType, proto: FunProto, resultType: Type)(implicit ctx: Context) extends TypedApply(app, fun, methRef, proto.args, resultType) { def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal.widenExpr) def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg) @@ -666,7 +666,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } /** Subclass of Application for type checking an Apply node with typed arguments. */ - class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context) + class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: NamedType, args: List[Tree], resultType: Type)(implicit ctx: Context) extends TypedApply[Type](app, fun, methRef, args, resultType) { // Dotty deviation: Dotc infers Untyped for the supercall. This seems to be according to the rules // (of both Scala and Dotty). Untyped is legal, and a subtype of Typed, whereas TypeApply @@ -681,7 +681,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * otherwise the current context. */ def argCtx(app: untpd.Tree)(implicit ctx: Context): Context = - if (untpd.isSelfConstrCall(app)) ctx.thisCallArgContext else ctx + (if (untpd.isSelfConstrCall(app)) ctx.thisCallArgContext else ctx).retractMode(Mode.Type) /** Typecheck application. Result could be an `Apply` node, * or, if application is an operator assignment, also an `Assign` or @@ -691,7 +691,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def realApply(implicit ctx: Context): Tree = track("realApply") { val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree)) - val fun1 = typedExpr(tree.fun, originalProto) + val fun1 = typed(tree.fun, originalProto)(ctx.retractMode(Mode.Pattern)) // Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as // a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application, @@ -708,13 +708,13 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // help sharpen the inferred parameter types for the argument function literal(s). // This tweak is needed to make i1378 compile. if (tree.args.exists(untpd.isFunctionWithUnknownParamType(_))) - if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt))) + if (!constrainResult(tree.symbol, fun1.tpe.widen, proto.derivedFunProto(resultType = pt))) typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt") /** Type application where arguments come from prototype, and no implicits are inserted */ def simpleApply(fun1: Tree, proto: FunProto)(implicit ctx: Context): Tree = methPart(fun1).tpe match { - case funRef: TermRef => + case funRef: NamedType => val app = if (proto.allArgTypesAreCurrent()) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) @@ -1280,12 +1280,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * section conforms to the expected type `resultType`? If `resultType` * is a `IgnoredProto`, pick the underlying type instead. */ - def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match { - case IgnoredProto(ignored) => resultConforms(alt, ignored) + def resultConforms(altSym: Symbol, altType: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match { + case IgnoredProto(ignored) => resultConforms(altSym, altType, ignored) case _: ValueType => - alt.widen match { - case tp: PolyType => resultConforms(constrained(tp).resultType, resultType) - case tp: MethodType => constrainResult(tp.resultType, resultType) + altType.widen match { + case tp: PolyType => resultConforms(altSym, constrained(tp).resultType, resultType) + case tp: MethodType => constrainResult(altSym, tp.resultType, resultType) case _ => true } case _ => true @@ -1304,9 +1304,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * do they prune much, on average. */ def adaptByResult(chosen: TermRef) = pt match { - case pt: FunProto if !ctx.test(implicit ctx => resultConforms(chosen, pt.resultType)) => + case pt: FunProto if !ctx.test(implicit ctx => resultConforms(chosen.symbol, chosen, pt.resultType)) => val conformingAlts = alts.filter(alt => - (alt ne chosen) && ctx.test(implicit ctx => resultConforms(alt, pt.resultType))) + (alt ne chosen) && ctx.test(implicit ctx => resultConforms(alt.symbol, alt, pt.resultType))) conformingAlts match { case Nil => chosen case alt2 :: Nil => alt2 @@ -1362,9 +1362,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => alts filter (isApplicable(_, argTypes, resultType)) val candidates = pt match { - case pt @ FunProto(args, resultType, _) => - val numArgs = args.length - val normArgs = args.mapConserve { + case pt: FunProto => + val numArgs = pt.args.length + val normArgs = pt.args.mapConserve { case Block(Nil, expr) => expr case x => x } @@ -1388,8 +1388,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def narrowByShapes(alts: List[TermRef]): List[TermRef] = { if (normArgs exists untpd.isFunctionWithUnknownParamType) - if (hasNamedArg(args)) narrowByTrees(alts, args map treeShape, resultType) - else narrowByTypes(alts, normArgs map typeShape, resultType) + if (hasNamedArg(normArgs)) narrowByTrees(alts, normArgs map treeShape, pt.resultType) + else narrowByTypes(alts, normArgs map typeShape, pt.resultType) else alts } @@ -1415,7 +1415,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => if (isDetermined(alts2)) alts2 else { pretypeArgs(alts2, pt) - narrowByTrees(alts2, pt.typedArgs, resultType) + narrowByTrees(alts2, pt.typedArgs, pt.resultType) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e909906a8b7a..aa6ffaff1686 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -33,9 +33,8 @@ import dotty.tools.dotc.transform.ValueClasses._ object Checking { import tpd._ - /** A general checkBounds method that can be used for TypeApply nodes as - * well as for AppliedTypeTree nodes. Also checks that type arguments to - * *-type parameters are fully applied. + /** A general checkBounds method that can be used for TypeApply nodes in term and type form. + * Also checks that type arguments to *-type parameters are fully applied. */ def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): Unit = { (args, boundss).zipped.foreach { (arg, bound) => @@ -51,8 +50,6 @@ object Checking { } /** Check that type arguments `args` conform to corresponding bounds in `tl` - * Note: This does not check the bounds of AppliedTypeTrees. These - * are handled by method checkBounds in FirstTransform */ def checkBounds(args: List[tpd.Tree], tl: TypeLambda)(implicit ctx: Context): Unit = checkBounds(args, tl.paramInfos, _.substParams(tl, _)) @@ -64,8 +61,8 @@ object Checking { * Unreducible applications correspond to general existentials, and we * cannot handle those. */ - def checkAppliedType(tree: AppliedTypeTree, boundsCheck: Boolean)(implicit ctx: Context) = { - val AppliedTypeTree(tycon, args) = tree + def checkAppliedType(tree: TypeApply, boundsCheck: Boolean)(implicit ctx: Context) = { + val TypeApply(tycon, args) = tree // If `args` is a list of named arguments, return corresponding type parameters, // otherwise return type parameters unchanged val tparams = tycon.tpe.typeParams diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 31fd2d2026e5..a57534cba51b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1103,19 +1103,24 @@ trait Implicits { self: Typer => val eligible = if (contextual) ctx.implicits.eligible(wildProto) else implicitScope(wildProto).eligible - searchImplicits(eligible, contextual).recoverWith { - failure => failure.reason match { - case _: AmbiguousImplicits => failure - case reason => - if (contextual) - bestImplicit(contextual = false).recoverWith { - failure2 => reason match { - case (_: DivergingImplicit) | (_: ShadowedImplicit) => failure - case _ => failure2 + searchImplicits(eligible, contextual) match { + case result: SearchSuccess => + if (contextual && ctx.mode.is(Mode.TransparentBody)) + Inliner.markContextualImplicit(result.tree) + result + case failure: SearchFailure => + failure.reason match { + case _: AmbiguousImplicits => failure + case reason => + if (contextual) + bestImplicit(contextual = false).recoverWith { + failure2 => reason match { + case (_: DivergingImplicit) | (_: ShadowedImplicit) => failure + case _ => failure2 + } } - } - else failure - } + else failure + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 9b6c128d023b..a288fe2578aa 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -146,7 +146,7 @@ object Inferencing { def inferTypeParams(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree.tpe match { case tl: TypeLambda => val (tl1, tvars) = constrained(tl, tree) - var tree1 = AppliedTypeTree(tree.withType(tl1), tvars) + var tree1 = TypeApply(tree.withType(tl1), tvars) tree1.tpe <:< pt fullyDefinedType(tree1.tpe, "template parent", tree.pos) tree1 @@ -408,7 +408,7 @@ trait Inferencing { this: Typer => val resultAlreadyConstrained = tree.isInstanceOf[Apply] || tree.tpe.isInstanceOf[MethodOrPoly] if (!resultAlreadyConstrained) - constrainResult(tree.tpe, pt) + constrainResult(tree.symbol, tree.tpe, pt) // This is needed because it could establish singleton type upper bounds. See i2998.scala. val tp = tree.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index ca87c1f58c42..1792022d44ef 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -27,10 +27,31 @@ import collection.mutable import transform.TypeUtils._ import reporting.trace import util.Positions.Position +import util.Property +import ast.TreeInfo object Inliner { import tpd._ + /** Marks an implicit reference found in the context (as opposed to the implicit scope) + * from an inlineable body. Such references will be carried along with the body to + * the expansion site. + */ + private val ContextualImplicit = new Property.StickyKey[Unit] + + def markContextualImplicit(tree: Tree)(implicit ctx: Context): Unit = + if (!defn.ScalaPredefModule.moduleClass.derivesFrom(tree.symbol.maybeOwner)) + methPart(tree).putAttachment(ContextualImplicit, ()) + + /** A key to be used in a context property that provides a map from enclosing implicit + * value bindings to their right hand sides. + */ + private val InlineBindings = new Property.Key[MutableSymbolMap[Tree]] + + /** A map from the symbols of all enclosing inline value bindings to their right hand sides */ + def inlineBindings(implicit ctx: Context): MutableSymbolMap[Tree] = + ctx.property(InlineBindings).get + class InlineAccessors extends AccessProxies { /** If an inline accessor name wraps a unique inline name, this is taken as indication @@ -60,7 +81,9 @@ object Inliner { def postTransform(tree: Tree)(implicit ctx: Context) = tree match { case Assign(lhs, rhs) if lhs.symbol.name.is(InlineAccessorName) => - cpy.Apply(tree)(useSetter(lhs), rhs :: Nil) + val setter = useSetter(lhs) + if (inlineSym.isTransparentMethod) tree // just generate a setter, but don't integrate it in the tree + else cpy.Apply(tree)(setter, rhs :: Nil) case _ => tree } @@ -212,7 +235,7 @@ object Inliner { * to have the inlined method as owner. */ def registerInlineInfo( - inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { + inlined: Symbol, originalBody: untpd.Tree, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { inlined.unforcedAnnotation(defn.BodyAnnot) match { case Some(ann: ConcreteBodyAnnotation) => case Some(ann: LazyBodyAnnotation) if ann.isEvaluated => @@ -221,18 +244,130 @@ object Inliner { val inlineCtx = ctx inlined.updateAnnotation(LazyBodyAnnotation { _ => implicit val ctx = inlineCtx - val body = treeExpr(ctx) - if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body) + val rawBody = treeExpr(ctx) + val typedBody = + if (ctx.reporter.hasErrors) rawBody + else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody) + val inlineableBody = + if (inlined.isInlinedMethod) typedBody + else addReferences(inlined, originalBody, typedBody) + inlining.println(i"Body to inline for $inlined: $inlineableBody") + inlineableBody }) } } } + /** Tweak untyped tree `original` so that all external references are typed + * and it reflects the changes in the corresponding typed tree `typed` that + * make `typed` inlineable. Concretely: + * + * - all external references via identifiers or this-references are converted + * to typed splices, + * - if X gets an inline accessor in `typed`, references to X in `original` + * are converted to the inline accessor name. + */ + def addReferences(inlineMethod: Symbol, + original: untpd.Tree, typed: tpd.Tree)(implicit ctx: Context): tpd.Tree = { + + def isExternal(sym: Symbol) = sym.exists && !isLocal(sym, inlineMethod) + + // Maps from positions to external reference types and inline selector names. + object referenced extends TreeTraverser { + val typeAtPos = mutable.Map[Position, Type]() + val nameAtPos = mutable.Map[Position, Name]() + val implicitSyms = mutable.Set[Symbol]() + val implicitRefs = new mutable.ListBuffer[Tree] + def registerIfContextualImplicit(tree: Tree) = tree match { + case tree: RefTree + if tree.removeAttachment(ContextualImplicit).isDefined && + isExternal(tree.symbol) && + !implicitSyms.contains(tree.symbol) => + if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod))) + ctx.warning("implicit reference $tree is dropped at inline site because it refers to local symbol(s)", tree.pos) + else { + implicitSyms += tree.symbol + implicitRefs += tree + } + case _ => + } + def traverse(tree: Tree)(implicit ctx: Context): Unit = { + tree match { + case _: Ident | _: This => + //println(i"leaf: $tree at ${tree.pos}") + if (isExternal(tree.symbol)) { + if (ctx.debug) inlining.println(i"type at pos ${tree.pos.toSynthetic} = ${tree.tpe}") + typeAtPos(tree.pos.toSynthetic) = tree.tpe + } + case _: Select if tree.symbol.name.is(InlineAccessorName) => + inlining.println(i"accessor: $tree at ${tree.pos}") + nameAtPos(tree.pos.toSynthetic) = tree.symbol.name + case _ => + } + registerIfContextualImplicit(tree) + traverseChildren(tree) + } + } + referenced.traverse(typed) + + // The untyped tree transform that applies the tweaks + object addRefs extends untpd.UntypedTreeMap { + override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = { + def adjustLeaf(tree: untpd.Tree): untpd.Tree = referenced.typeAtPos.get(tree.pos.toSynthetic) match { + case Some(tpe) => untpd.TypedSplice(tree.withType(tpe)) + case none => tree + } + def adjustName(name: Name) = referenced.nameAtPos.get(tree.pos.toSynthetic) match { + case Some(n) => n + case none => name + } + def adjustQualifier(tree: untpd.Tree): untpd.Tree = tree match { + case tree @ Ident(name1) => + referenced.typeAtPos.get(tree.pos.startPos) match { + case Some(tp: ThisType) => + val qual = untpd.TypedSplice(This(tp.cls).withPos(tree.pos.startPos)) + cpy.Select(tree)(qual, name1) + case none => + tree + } + case tree => tree + } + val tree1 = super.transform(tree) + tree1 match { + case This(_) => + adjustLeaf(tree1) + case Ident(name) => + adjustQualifier(adjustLeaf(cpy.Ident(tree1)(adjustName(name)))) + case Select(pre, name) => + cpy.Select(tree1)(pre, adjustName(name)) + case tree: untpd.DerivedTypeTree => + inlining.println(i"inlining derived $tree --> ${ctx.typer.typed(tree)}") + untpd.TypedSplice(ctx.typer.typed(tree)) + case _ => + tree1 + } + } + } + val implicitBindings = + for (iref <- referenced.implicitRefs.toList) yield { + val localImplicit = iref.symbol.asTerm.copy( + owner = inlineMethod, + name = UniqueInlineName.fresh(iref.symbol.name.asTermName), + flags = Implicit | Method | Stable, + info = iref.symbol.info.ensureMethodic, + coord = inlineMethod.pos).asTerm + polyDefDef(localImplicit, tps => vrefss => + iref.appliedToTypes(tps).appliedToArgss(vrefss)) + } + val untpdSplice = tpd.UntypedSplice(addRefs.transform(original)).withType(typed.tpe) + seq(implicitBindings, untpdSplice) + } + /** `sym` has an inline method with a known body to inline (note: definitions coming * from Scala2x class files might be `@inline`, but still lack that body. */ def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean = - sym.isInlinedMethod && sym.hasAnnotation(defn.BodyAnnot) // TODO: Open this up for transparent methods as well + sym.isInlineableMethod && sym.hasAnnotation(defn.BodyAnnot) /** The body to inline for method `sym`. * @pre hasBodyToInline(sym) @@ -240,6 +375,23 @@ object Inliner { def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree = sym.unforcedAnnotation(defn.BodyAnnot).get.tree + /** Should call with method `meth` be inlined in this context? */ + def isInlineable(meth: Symbol)(implicit ctx: Context): Boolean = { + + def suppressInline = + ctx.owner.ownersIterator.exists(_.isInlineableMethod) || + meth.isTransparentMethod && ctx.mode.is(Mode.NoInlineTransparent) || + ctx.settings.YnoInline.value || + ctx.isAfterTyper || + ctx.reporter.hasErrors + + hasBodyToInline(meth) && !suppressInline + } + + /** Is `meth` a transparent method that should be inlined in this context? */ + def isTransparentInlineable(meth: Symbol)(implicit ctx: Context): Boolean = + meth.isTransparentMethod && isInlineable(meth) + /** Try to inline a call to a `@inline` method. Fail with error if the maximal * inline depth is exceeded. * @@ -251,13 +403,20 @@ object Inliner { def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) { val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors - if (ctx.reporter.hasErrors) tree else new Inliner(tree, body).inlined(pt) + if (ctx.reporter.hasErrors) tree + else { + val inlinerCtx = + if (ctx.property(InlineBindings).isDefined) ctx + else ctx.fresh.setProperty(InlineBindings, newMutableSymbolMap[Tree]) + new Inliner(tree, body)(inlinerCtx).inlined(pt) + } } else errorTree( tree, i"""|Maximal number of successive inlines (${ctx.settings.XmaxInlines.value}) exceeded, |Maybe this is caused by a recursive inline method? - |You can use -Xmax:inlines to change the limit.""" + |You can use -Xmax:inlines to change the limit.""", + (tree :: enclosingInlineds).last.pos ) /** Replace `Inlined` node by a block that contains its bindings and expansion */ @@ -326,7 +485,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { /** A buffer for bindings that define proxies for actual arguments */ val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] - computeParamBindings(meth.info, targs, argss) + computeParamBindings(meth.info.hiBound, targs, argss) private def newSym(name: Name, flags: FlagSet, info: Type): Symbol = ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos) @@ -361,12 +520,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { * proxies to this-references. */ private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Unit = tp match { - case tp: PolyType => + case tp: TypeLambda => (tp.paramNames, targs).zipped.foreach { (name, arg) => paramBinding(name) = arg.tpe.stripTypeVar } computeParamBindings(tp.resultType, Nil, argss) - case tp: MethodType => + case tp: TermLambda => (tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) => paramBinding(name) = arg.tpe.dealias match { case _: SingletonType if isIdempotentExpr(arg) => arg.tpe @@ -489,23 +648,67 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => tree }} - val inlineCtx = inlineContext(call) + val inlineTyper = if (meth.isTransparentMethod) new TransparentTyper else new InlineReTyper + val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope + // The complete translation maps references to `this` and parameters to // corresponding arguments or proxies on the type and term level. It also changes // the owner from the inlined method to the current owner. val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)(inlineCtx) - val expansion = inliner(rhsToInline.withPos(call.pos)) - trace(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) { + val expansion = inliner.transform(rhsToInline.withPos(call.pos)) match { + case Block(implicits, tpd.UntypedSplice(expansion)) => + val prevOwners = implicits.map(_.symbol.owner).distinct + val localizer = new TreeTypeMap(oldOwners = prevOwners, newOwners = prevOwners.map(_ => ctx.owner)) + val (_, implicits1) = localizer.transformDefs(implicits) + for (idef <- implicits1) { + bindingsBuf += idef.withType(idef.symbol.typeRef).asInstanceOf[ValOrDefDef] + // Note: Substituting new symbols does not automatically lead to good prefixes + // if the previous symbol was owned by a class. That's why we need to set the type + // of `idef` explicitly. It would be nice if substituters were smarter, but + // it seems non-trivial to come up with rules that work in all cases. + inlineCtx.enter(idef.symbol) + } + expansion + case tpd.UntypedSplice(expansion) => + expansion + case expansion => + expansion + } - // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details. - val expansion1 = InlineTyper.typed(expansion, pt)(inlineCtx) + /** If this is a value binding: + * - reduce its rhs if it is a projection and adjust its type accordingly, + * - record symbol -> rhs in the InlineBindings context propery. + * Also, set position to the one of the inline call. + */ + def normalizeBinding(binding: ValOrDefDef)(implicit ctx: Context) = { + val binding1 = binding match { + case binding: ValDef => + val rhs1 = reduceProjection(binding.rhs) + inlineBindings(inlineCtx).put(binding.symbol, rhs1) + if (rhs1 `eq` binding.rhs) binding + else { + binding.symbol.info = rhs1.tpe + cpy.ValDef(binding)(tpt = TypeTree(rhs1.tpe), rhs = rhs1) + } + case _ => + binding + } + binding1.withPos(call.pos) + } + + trace(i"inlining $call", inlining, show = true) { - /** All bindings in `bindingsBuf` except bindings of inlineable closures */ - val bindings = bindingsBuf.toList.map(_.withPos(call.pos)) + /** All bindings in `bindingsBuf` */ + val bindings = bindingsBuf.toList.map(normalizeBinding) - inlining.println(i"original bindings = $bindings%\n%") - inlining.println(i"original expansion = $expansion1") + // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details. + val expansion1 = inlineTyper.typed(expansion, pt)(inlineCtx) + + if (ctx.settings.verbose.value) { + inlining.println(i"original bindings = $bindings%\n%") + inlining.println(i"original expansion = $expansion1") + } val (finalBindings, finalExpansion) = dropUnusedDefs(bindings, expansion1) @@ -513,14 +716,78 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } } + /** An extractor for terms equivalent to `new C(args)`, returning the class `C` + * and the arguments `args`. Can see inside blocks and Inlined nodes and can + * follow a reference to an inline value binding to its right hand side. + */ + object NewInstance { + def unapply(tree: Tree)(implicit ctx: Context): Option[(Symbol, List[Tree], List[Tree])] = { + def unapplyLet(bindings: List[Tree], expr: Tree) = + unapply(expr) map { + case (cls, reduced, prefix) => (cls, reduced, bindings ::: prefix) + } + tree match { + case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => + Some((tpt.tpe.classSymbol, args, Nil)) + case Ident(_) => + inlineBindings.get(tree.symbol).flatMap(unapply) + case Inlined(_, bindings, expansion) => + unapplyLet(bindings, expansion) + case Block(stats, expr) if isPureExpr(tree) => + unapplyLet(stats, expr) + case _ => + None + } + } + } + + /** If we are inlining a transparent method and `tree` is equivalent to `new C(args).x` + * where class `C` does not have initialization code and `x` is a parameter corresponding + * to one of the arguments `args`, the corresponding argument, prefixed by the evaluation + * of impure arguments, otherwise `tree` itself. + */ + def reduceProjection(tree: Tree)(implicit ctx: Context): Tree = { + if (meth.isTransparentMethod) { + if (ctx.debug) inlining.println(i"try reduce projection $tree") + tree match { + case Select(NewInstance(cls, args, prefix), field) if cls.isNoInitsClass => + def matches(param: Symbol, selection: Symbol): Boolean = + param == selection || { + selection.name match { + case InlineAccessorName(underlying) => + param.name == underlying && selection.info.isInstanceOf[ExprType] + case _ => + false + } + } + val idx = cls.asClass.paramAccessors.indexWhere(matches(_, tree.symbol)) + if (idx >= 0 && idx < args.length) { + def collectImpure(from: Int, end: Int) = + (from until end).filterNot(i => isPureExpr(args(i))).toList.map(args) + val leading = collectImpure(0, idx) + val trailing = collectImpure(idx + 1, args.length) + val arg = args(idx) + val argInPlace = + if (trailing.isEmpty) arg + else letBindUnless(TreeInfo.Pure, arg)(seq(trailing, _)) + val reduced = seq(prefix, seq(leading, argInPlace)) + inlining.println(i"projecting $tree -> ${reduced}") + return reduced + } + case _ => + } + } + tree + } + /** An extractor for references to inlineable arguments. These are : * - by-value arguments marked with `inline` * - all by-name arguments */ private object InlineableArg { lazy val paramProxies = paramProxy.values.toSet - def unapply(tree: Ident)(implicit ctx: Context): Option[Tree] = - if (paramProxies.contains(tree.tpe)) + def unapply(tree: Trees.Ident[_])(implicit ctx: Context): Option[Tree] = + if (paramProxies.contains(tree.typeOpt)) bindingsBuf.find(_.name == tree.name) match { case Some(vdef: ValDef) if vdef.symbol.is(Inline) => Some(vdef.rhs.changeOwner(vdef.symbol, ctx.owner)) @@ -531,14 +798,28 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { else None } - /** A typer for inlined code. Its purpose is: + /** A base trait of InlineTyper and InlineReTyper containing operations that + * work the same way for both. Beyond typing or retyping, an inline typer performs + * the following functions: + * * 1. Implement constant folding over inlined code * 2. Selectively expand ifs with constant conditions * 3. Inline arguments that are by-name closures * 4. Make sure inlined code is type-correct. * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed) */ - private object InlineTyper extends ReTyper { + trait InlineTyping extends Typer { + + protected def tryInline(tree: tpd.Tree)(implicit ctx: Context) = tree match { + case InlineableArg(rhs) => + inlining.println(i"inline arg $tree -> $rhs") + rhs + case _ => + EmptyTree + } + + /** The context to be used for sub-expressions that are not in redex position. */ + protected def noRedexCtx(implicit ctx: Context): Context override def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { tpe match { @@ -552,30 +833,17 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { super.ensureAccessible(tpe, superAccess, pos) } - override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = - tree.asInstanceOf[tpd.Tree] match { - case InlineableArg(rhs) => inlining.println(i"inline arg $tree -> $rhs"); rhs - case _ => super.typedIdent(tree, pt) - } - - override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { - assert(tree.hasType, tree) - val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this)) - val res = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) - ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos) - res - } - override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = { - val cond1 = typed(tree.cond, defn.BooleanType) + val cond1 = typedExpr(tree.cond, defn.BooleanType) cond1.tpe.widenTermRefExpr match { case ConstantType(Constant(condVal: Boolean)) => - val selected = typed(if (condVal) tree.thenp else tree.elsep, pt) + var selected = typed(if (condVal) tree.thenp else tree.elsep, pt) + if (selected.isEmpty) selected = tpd.Literal(Constant(())) if (isIdempotentExpr(cond1)) selected else Block(cond1 :: Nil, selected) case _ => val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1)) - super.typedIf(if1, pt) + super.typedIf(if1, pt)(noRedexCtx) } } @@ -594,8 +862,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { */ def betaReduce(tree: Tree) = tree match { case Apply(Select(cl @ closureDef(ddef), nme.apply), args) if defn.isFunctionType(cl.tpe) => - ddef.tpe.widen match { - case mt: MethodType if ddef.vparamss.head.length == args.length => + ddef.tpe.widen.toLambda match { + case mt: TermLambda if ddef.vparamss.head.length == args.length => val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] val argSyms = (mt.paramNames, mt.paramInfos, args).zipped.map { (name, paramtp, arg) => arg.tpe.dealias match { @@ -614,8 +882,57 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { case _ => tree } - betaReduce(super.typedApply(tree, pt)) + constToLiteral(betaReduce(super.typedApply(tree, pt))) + } + } + + /** A full typer used for transparent methods */ + private class TransparentTyper extends Typer with InlineTyping { + + /** Transparent methods don't inline recursively unless in redex position */ + def noRedexCtx(implicit ctx: Context) = ctx.addMode(Mode.NoInlineTransparent) + + override def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree = + reduceProjection(tryInline(tree.splice) `orElse` super.typedTypedSplice(tree)) + + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context) = + constToLiteral(reduceProjection(super.typedSelect(tree, pt))) + + override def typedValDef(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = { + import untpd.modsDeco + super.typedValDef(tree, sym)(if (tree.mods.is(Lazy)) noRedexCtx else ctx) + } + + override def typedDefDef(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + super.typedDefDef(tree, sym)(noRedexCtx) + + override def typedTypeDef(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = + super.typedTypeDef(tree, sym)(noRedexCtx) + + override def typedClassDef(tree: untpd.TypeDef, sym: ClassSymbol)(implicit ctx: Context) = + super.typedClassDef(tree, sym)(noRedexCtx) + } + + /** A re-typer used for inlined methods */ + private class InlineReTyper extends ReTyper with InlineTyping { + + /** Inline methods always expand all recursive inline calls, whether in redex + * position or not. + */ + def noRedexCtx(implicit ctx: Context) = ctx + + override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = + tryInline(tree.asInstanceOf[tpd.Tree]) `orElse` super.typedIdent(tree, pt) + + override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { + assert(tree.hasType, tree) + val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this)) + val res = untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) + ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos) + res } + + override def newLikeThis: Typer = new InlineReTyper } /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 342061d48471..a6ecf1ff34d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -123,7 +123,7 @@ trait NamerContextOps { this: Context => else given /** if isConstructor, make sure it has one non-implicit parameter list */ - def normalizeIfConstructor(termParamss: List[List[Symbol]], isConstructor: Boolean) = + def normalizeIfConstructor(termParamss: List[List[TermSymbol]], isConstructor: Boolean) = if (isConstructor && (termParamss.isEmpty || termParamss.head.nonEmpty && (termParamss.head.head is Implicit))) Nil :: termParamss @@ -131,21 +131,29 @@ trait NamerContextOps { this: Context => termParamss /** The method type corresponding to given parameters and result type */ - def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(implicit ctx: Context): Type = { + def lambdaType(typeParams: List[TypeSymbol], valueParamss: List[List[TermSymbol]], resultType: Type, sym: Symbol)(implicit ctx: Context): Type = { val monotpe = (valueParamss :\ resultType) { (params, resultType) => - val (isImplicit, isErased) = - if (params.isEmpty) (false, false) - else (params.head is Implicit, params.head is Erased) - val make = MethodType.maker(isJava, isImplicit, isErased) - if (isJava) - for (param <- params) - if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType - make.fromSymbols(params.asInstanceOf[List[TermSymbol]], resultType) + if (sym.isTerm) { + val isJava = sym.flagsUNSAFE.is(JavaDefined) + val (isImplicit, isErased) = + if (params.isEmpty) (false, false) + else (params.head is Implicit, params.head is Erased) + val make = MethodType.maker(isJava, isImplicit, isErased) + if (isJava) + for (param <- params) + if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType + make.fromSymbols(params, resultType) + } + else HKTermLambda.fromParams(params, resultType) } - if (typeParams.nonEmpty) PolyType.fromParams(typeParams.asInstanceOf[List[TypeSymbol]], monotpe) - else if (valueParamss.isEmpty) ExprType(monotpe) - else monotpe + if (sym.isTerm) + if (typeParams.nonEmpty) PolyType.fromParams(typeParams, monotpe) + else if (valueParamss.isEmpty) ExprType(monotpe) + else monotpe + else + if (typeParams.nonEmpty) HKTypeLambda.fromParams(typeParams, monotpe) + else monotpe } /** Add moduleClass or sourceModule functionality to completer @@ -276,10 +284,14 @@ class Namer { typer: Typer => def checkFlags(flags: FlagSet) = if (flags.isEmpty) flags else { - val (ok, adapted, kind) = tree match { - case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type") - case _ => (flags.isTermFlags, flags.toTermFlags, "value") + val isType = tree match { + case tree: TypeDef => true + case tree: DefDef => tree.name.isTypeName + case _ => false } + val (ok, adapted, kind) = + if (isType) (flags.isTypeFlags, flags.toTypeFlags, "type") + else (flags.isTermFlags, flags.toTermFlags, "value") if (!ok) ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos) adapted @@ -341,11 +353,10 @@ class Namer { typer: Typer => val flags = checkFlags(tree.mods.flags) val isDeferred = lacksDefinition(tree) val deferred = if (isDeferred) Deferred else EmptyFlags - val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags - val higherKinded = tree match { - case TypeDef(_, LambdaTypeTree(_, _)) if isDeferred => HigherKinded - case _ => EmptyFlags - } + val method = + if (!tree.isInstanceOf[DefDef]) EmptyFlags + else if (name.isTypeName) TypeMethod | Transparent + else Method // to complete a constructor, move one context further out -- this // is the context enclosing the class. Note that the context in which a @@ -363,7 +374,7 @@ class Namer { typer: Typer => case _ => new Completer(tree)(cctx) } val info = adjustIfModule(completer, tree) - createOrRefine[Symbol](tree, name, flags | deferred | method | higherKinded, + createOrRefine[Symbol](tree, name, flags | deferred | method, _ => info, (fs, _, pwithin) => ctx.newSymbol(ctx.owner, name, fs, info, pwithin, tree.namePos)) case tree: Import => @@ -826,6 +837,7 @@ class Namer { typer: Typer => case original: untpd.DefDef if sym.isInlineableMethod => Inliner.registerInlineInfo( sym, + original.rhs, implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs )(localContext(sym)) case _ => @@ -992,6 +1004,8 @@ class Namer { typer: Typer => if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing + cls.setNoInitsFlags(parentsKind(parents), bodyKind(rest)) + if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(Stable) } } @@ -1013,6 +1027,9 @@ class Namer { typer: Typer => def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits)) + def typedAheadRHS(rhs: Tree, pt: Type = WildcardType, sym: Symbol)(implicit ctx: Context): tpd.Tree = + if (sym.isTerm) typedAheadExpr(rhs, pt) else typedAheadType(rhs, pt) + def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): tpd.Tree = typedAheadExpr(tree, defn.AnnotationType) @@ -1057,7 +1074,7 @@ class Namer { typer: Typer => * NoType if neither case holds. */ val inherited = - if (sym.owner.isTerm) NoType + if (sym.owner.isTerm || sym.isType) NoType else { // TODO: Look only at member of supertype instead? lazy val schema = paramFn(WildcardType) @@ -1094,7 +1111,7 @@ class Namer { typer: Typer => * the corresponding parameter where bound parameters are replaced by * Wildcards. */ - def rhsProto = sym.asTerm.name collect { + def rhsProto = sym.name collect { case DefaultGetterName(original, idx) => val meth: Denotation = if (original.isConstructorName && (sym.owner is ModuleClass)) @@ -1117,13 +1134,14 @@ class Namer { typer: Typer => // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - def isInline = sym.is(FinalOrInlineOrTransparent, butNot = Method | Mutable) + def isInlineVal = + sym.isTerm && sym.is(FinalOrInlineOrTransparent, butNot = Method | Mutable) // Widen rhs type and eliminate `|' but keep ConstantTypes if // definition is inline (i.e. final in Scala2) and keep module singleton types // instead of widening to the underlying module class types. def widenRhs(tp: Type): Type = tp.widenTermRefExpr match { - case ctp: ConstantType if isInline => ctp + case ctp: ConstantType if isInlineVal => ctp case ref: TypeRef if ref.symbol.is(ModuleClass) => tp case _ => tp.widen.widenUnion } @@ -1132,8 +1150,11 @@ class Namer { typer: Typer => // it would be erased to BoxedUnit. def dealiasIfUnit(tp: Type) = if (tp.isRef(defn.UnitClass)) defn.UnitType else tp - val rhsCtx = ctx.addMode(Mode.InferringReturnType) - def rhsType = typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe + var rhsCtx = ctx.addMode(Mode.InferringReturnType) + if (sym.isTransparentMethod) rhsCtx = rhsCtx.addMode(Mode.TransparentBody) + def rhsType = + if (sym.isTerm) typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe + else typedAheadType(mdef.rhs)(rhsCtx).tpe // Approximate a type `tp` with a type that does not contain skolem types. val deskolemize = new ApproximatingTypeMap { @@ -1172,7 +1193,8 @@ class Namer { typer: Typer => case _: untpd.DerivedTypeTree => WildcardType case TypeTree() => - checkMembersOK(inferredType, mdef.pos) + if (sym.isType) TypeBounds.empty + else checkMembersOK(inferredType, mdef.pos) case DependentTypeTree(tpFun) => tpFun(paramss.head) case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) => @@ -1227,11 +1249,13 @@ class Namer { typer: Typer => for (tparam <- tparams) typedAheadExpr(tparam) vparamss foreach completeParams - def typeParams = tparams map symbolOfTree - val termParamss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor) + def typeParams = tparams.map(symbolOfTree(_).asType) + val termParamss = ctx.normalizeIfConstructor( + vparamss.nestedMap(symbolOfTree(_).asTerm), isConstructor) def wrapMethType(restpe: Type): Type = { + if (sym.isType) assert(restpe.isInstanceOf[TypeBounds], restpe) instantiateDependent(restpe, typeParams, termParamss) - ctx.methodType(tparams map symbolOfTree, termParamss, restpe, isJava = ddef.mods is JavaDefined) + ctx.lambdaType(typeParams, termParamss, restpe, sym) } if (isConstructor) { // set result type tree to unit, but take the current class as result type of the symbol diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 5a32fcf19364..a40151e6297c 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -71,6 +71,12 @@ object ProtoTypes { if (!res) ctx.typerState.resetConstraintTo(savedConstraint) res } + + /** Constrain result unless `meth` is a transparent method in an inlineable context. + * In the latter case we should inline before constraining the result. + */ + def constrainResult(meth: Symbol, mt: Type, pt: Type)(implicit ctx: Context): Boolean = + Inliner.isTransparentInlineable(meth) || constrainResult(mt, pt) } object NoViewsAllowed extends Compatibility { @@ -199,8 +205,9 @@ object ProtoTypes { * * [](args): resultType */ - case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context) + class FunProto(val args: List[untpd.Tree], val resType: Type, val typer: Typer)(implicit ctx: Context) extends UncachedGroundType with ApplyingProto { + private[this] var myTypedArgs: List[Tree] = Nil override def resultType(implicit ctx: Context) = resType @@ -300,7 +307,7 @@ object ProtoTypes { case pt: FunProto => pt case _ => - myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer) + myTupled = FunProto(untpd.Tuple(args) :: Nil, resultType, typer) tupled } @@ -334,6 +341,11 @@ object ProtoTypes { override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer) } + object FunProto { + def apply(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context) = + new FunProto(args, resType, typer)(ctx.retractMode(Mode.Type)) + } + /** A prototype for expressions that appear in function position * @@ -377,7 +389,7 @@ object ProtoTypes { } class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer) + untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)(ctx) /** A prototype for expressions [] that are type-parameterized: * @@ -478,7 +490,7 @@ object ProtoTypes { /** The result type of `mt`, where all references to parameters of `mt` are * replaced by either wildcards (if typevarsMissContext) or TypeParamRefs. */ - def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type = + def resultTypeApprox(mt: TermLambda)(implicit ctx: Context): Type = if (mt.isResultDependent) { def replacement(tp: Type) = if (ctx.mode.is(Mode.TypevarsMissContext) || diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index afc4e83439e1..5347c9e524e9 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -362,8 +362,8 @@ trait TypeAssigner { } def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { - val ownType = fn.tpe.widen match { - case fntpe: MethodType => + val ownType = fn.tpe.widen.toLambda match { + case fntpe: TermLambda => if (sameLength(fntpe.paramInfos, args) || ctx.phase.prev.relaxedTyping) if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes) else fntpe.resultType @@ -376,63 +376,72 @@ trait TypeAssigner { } def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { - val ownType = fn.tpe.widen match { - case pt: TypeLambda => - val paramNames = pt.paramNames - if (hasNamedArg(args)) { - val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap - - // Type arguments which are specified by name (immutable after this first loop) - val namedArgMap = new mutable.HashMap[Name, Type] - for (NamedArg(name, arg) <- args) - if (namedArgMap.contains(name)) - ctx.error(DuplicateNamedTypeParameter(name), arg.pos) - else if (!paramNames.contains(name)) - ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos) - else - namedArgMap(name) = arg.tpe - - // Holds indexes of non-named typed arguments in paramNames - val gapBuf = new mutable.ListBuffer[Int] - def nextPoly(idx: Int) = { - val newIndex = gapBuf.length - gapBuf += idx - // Re-index unassigned type arguments that remain after transformation - pt.paramRefs(newIndex) - } - - // Type parameters after naming assignment, conserving paramNames order - val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) => - namedArgMap.getOrElse(pname, nextPoly(idx)) - } - - val transform = new TypeMap { - def apply(t: Type) = t match { - case TypeParamRef(`pt`, idx) => normArgs(idx) - case _ => mapOver(t) + val ownType = + if (tree.isTerm) + fn.tpe.widen match { + case pt: TypeLambda => + val paramNames = pt.paramNames + if (hasNamedArg(args)) { + assert(fn.isTerm) + val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap + + // Type arguments which are specified by name (immutable after this first loop) + val namedArgMap = new mutable.HashMap[Name, Type] + for (NamedArg(name, arg) <- args) + if (namedArgMap.contains(name)) + ctx.error(DuplicateNamedTypeParameter(name), arg.pos) + else if (!paramNames.contains(name)) + ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos) + else + namedArgMap(name) = arg.tpe + + // Holds indexes of non-named typed arguments in paramNames + val gapBuf = new mutable.ListBuffer[Int] + def nextPoly(idx: Int) = { + val newIndex = gapBuf.length + gapBuf += idx + // Re-index unassigned type arguments that remain after transformation + pt.paramRefs(newIndex) + } + + // Type parameters after naming assignment, conserving paramNames order + val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) => + namedArgMap.getOrElse(pname, nextPoly(idx)) + } + + val transform = new TypeMap { + def apply(t: Type) = t match { + case TypeParamRef(`pt`, idx) => normArgs(idx) + case _ => mapOver(t) + } + } + val resultType1 = transform(pt.resultType) + if (gapBuf.isEmpty) resultType1 + else { + val gaps = gapBuf.toList + pt.derivedLambdaType( + gaps.map(paramNames), + gaps.map(idx => transform(pt.paramInfos(idx)).bounds), + resultType1) + } } - } - val resultType1 = transform(pt.resultType) - if (gapBuf.isEmpty) resultType1 - else { - val gaps = gapBuf.toList - pt.derivedLambdaType( - gaps.map(paramNames), - gaps.map(idx => transform(pt.paramInfos(idx)).bounds), - resultType1) - } - } - else { - val argTypes = args.tpes - if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes) - else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) + else { + val argTypes = args.tpes + if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes) + else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) + } + case err: ErrorType => + err + case _ => + //println(i"bad type: $fn: ${fn.symbol} / ${fn.symbol.isType} / ${fn.symbol.info}") // DEBUG + errorType(err.takesNoParamsStr(fn, "type "), tree.pos) } - case err: ErrorType => - err - case _ => - //println(i"bad type: $fn: ${fn.symbol} / ${fn.symbol.isType} / ${fn.symbol.info}") // DEBUG - errorType(err.takesNoParamsStr(fn, "type "), tree.pos) - } + else { + assert(!hasNamedArg(args)) + val tparams = fn.tpe.typeParams + if (sameLength(tparams, args)) fn.tpe.appliedTo(args.tpes) + else wrongNumberOfTypeArgs(fn.tpe, tparams, args, tree.pos) + } tree.withType(ownType) } @@ -505,15 +514,6 @@ trait TypeAssigner { tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, rt.recThis))) } - def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { - assert(!hasNamedArg(args)) - val tparams = tycon.tpe.typeParams - val ownType = - if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) - else wrongNumberOfTypeArgs(tycon.tpe, tparams, args, tree.pos) - tree.withType(ownType) - } - def assignType(tree: untpd.LambdaTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = tree.withType(HKTypeLambda.fromParams(tparamDefs.map(_.symbol.asType), body.tpe)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fa48b6315a63..3c8b515b15da 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -519,7 +519,7 @@ class Typer extends Namer checkClassType(tpt1.tpe, tpt1.pos, traitReq = false, stablePrefixReq = true) tpt1 match { - case AppliedTypeTree(_, targs) => + case TypeApply(_, targs) => for (targ @ TypeBoundsTree(_, _) <- targs) ctx.error(WildcardOnTypeArgumentNotAllowedOnNew(), targ.pos) case _ => @@ -663,7 +663,7 @@ class Typer extends Namer def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") { val (exprCtx, stats1) = typedBlockStats(tree.stats) - val expr1 = typedExpr(tree.expr, pt.notApplied)(exprCtx) + val expr1 = typed(tree.expr, pt.notApplied)(exprCtx) ensureNoLocalRefs( cpy.Block(tree)(stats1, expr1).withType(expr1.tpe), pt, localSyms(stats1)) } @@ -707,7 +707,7 @@ 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 cond1 = typedExpr(tree.cond, defn.BooleanType) 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) @@ -783,7 +783,7 @@ class Typer extends Namer val resTpt = TypeTree(mt.nonDependentResultApprox).withPos(body.pos) val typeArgs = params1.map(_.tpt) :+ resTpt val tycon = TypeTree(funCls.typeRef) - val core = assignType(cpy.AppliedTypeTree(tree)(tycon, typeArgs), tycon, typeArgs) + val core = assignType(cpy.TypeApply(tree)(tycon, typeArgs), tycon, typeArgs) val appMeth = ctx.newSymbol(ctx.owner, nme.apply, Synthetic | Deferred, mt) val appDef = assignType( untpd.DefDef(appMeth.name, Nil, List(params1), resultTpt, EmptyTree), @@ -796,7 +796,7 @@ class Typer extends Namer typedDependent(args.asInstanceOf[List[ValDef]])( ctx.fresh.setOwner(ctx.newRefinedClassSymbol(tree.pos)).setNewScope) case _ => - typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt) + typed(cpy.TypeApply(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt) } } @@ -1206,18 +1206,18 @@ class Typer extends Namer assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls) } - def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") { - val tpt1 = typed(tree.tpt, AnyTypeConstructorProto)(ctx.retractMode(Mode.Pattern)) - val tparams = tpt1.tpe.typeParams + def typedAppliedTypeTree(tree: untpd.TypeApply)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") { + val tycon = typed(tree.fun, AnyTypeConstructorProto)(ctx.retractMode(Mode.Pattern)) + val tparams = tycon.tpe.typeParams if (tparams.isEmpty) { - ctx.error(TypeDoesNotTakeParameters(tpt1.tpe, tree.args), tree.pos) - tpt1 + ctx.error(TypeDoesNotTakeParameters(tycon.tpe, tree.args), tree.pos) + tycon } else { var args = tree.args val args1 = { if (args.length != tparams.length) { - wrongNumberOfTypeArgs(tpt1.tpe, tparams, args, tree.pos) + wrongNumberOfTypeArgs(tycon.tpe, tparams, args, tree.pos) args = args.take(tparams.length) } def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { @@ -1226,7 +1226,7 @@ class Typer extends Namer (if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramInfo) else (arg, WildcardType) - if (tpt1.symbol.isClass) + if (tycon.symbol.isClass) tparam match { case tparam: Symbol => tparam.ensureCompleted() // This is needed to get the test `compileParSetSubset` to work @@ -1237,7 +1237,7 @@ class Typer extends Namer arg match { case TypeBoundsTree(EmptyTree, EmptyTree) if tparam.paramInfo.isLambdaSub && - tpt1.tpe.typeParamSymbols.nonEmpty && + tycon.tpe.typeParamSymbols.nonEmpty && !ctx.mode.is(Mode.Pattern) => // An unbounded `_` automatically adapts to type parameter bounds. This means: // If we have wildcard application C[_], where `C` is a class replace @@ -1245,7 +1245,7 @@ class Typer extends Namer // type parameter in `C`, avoiding any referemces to parameters of `C`. // The transform does not apply for patters, where empty bounds translate to // wildcard identifiers `_` instead. - res = res.withType(avoid(tparam.paramInfo, tpt1.tpe.typeParamSymbols)) + res = res.withType(avoid(tparam.paramInfo, tycon.tpe.typeParamSymbols)) case _ => } res @@ -1264,7 +1264,7 @@ class Typer extends Namer } val args2 = preCheckKinds(args1, paramBounds) // check that arguments conform to bounds is done in phase PostTyper - assignType(cpy.AppliedTypeTree(tree)(tpt1, args2), tpt1, args2) + assignType(cpy.TypeApply(tree)(tycon, args2), tycon, args2) } } @@ -1407,7 +1407,7 @@ class Typer extends Namer } } - def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { + def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedDefDef") { val DefDef(name, tparams, vparamss, tpt, _) = ddef completeAnnotations(ddef, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) @@ -1428,10 +1428,13 @@ class Typer extends Namer (tparams1, sym.owner.typeParams).zipped.foreach ((tdef, tparam) => rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef))) } - val rhs1 = normalizeErasedRhs(typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx), sym) + if (sym.isTransparentMethod) rhsCtx = rhsCtx.addMode(Mode.TransparentBody) + val rhs1 = + if (sym.isTerm) normalizeErasedRhs(typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx), sym) + else typedType(ddef.rhs, tpt1.tpe)(rhsCtx) // Overwrite inline body to make sure it is not evaluated twice - if (sym.isInlineableMethod) Inliner.registerInlineInfo(sym, _ => rhs1) + if (sym.isInlineableMethod) Inliner.registerInlineInfo(sym, ddef.rhs, _ => rhs1) if (sym.isConstructor && !sym.isPrimaryConstructor) for (param <- tparams1 ::: vparamss1.flatten) @@ -1534,8 +1537,6 @@ class Typer extends Namer val dummy = localDummy(cls, impl) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(ctx.inClassContext(self1.symbol))) - if (!ctx.isAfterTyper) - cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat))) // Expand comments and type usecases if `-Ycook-comments` is set. if (ctx.settings.YcookComments.value) { @@ -1665,7 +1666,7 @@ class Typer extends Namer } def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree = - tree.tree match { + tree.splice match { case tree1: TypeTree => tree1 // no change owner necessary here ... case tree1: Ident => tree1 // ... or here, since these trees cannot contain bindings case tree1 => @@ -1804,7 +1805,8 @@ class Typer extends Namer case tree: untpd.Return => typedReturn(tree) case tree: untpd.Try => typedTry(tree, pt) case tree: untpd.Throw => typedThrow(tree) - case tree: untpd.TypeApply => typedTypeApply(tree, pt) + case tree: untpd.TypeApply => + if (tree.isTerm) typedTypeApply(tree, pt) else typedAppliedTypeTree(tree) case tree: untpd.Super => typedSuper(tree, pt) case tree: untpd.SeqLiteral => typedSeqLiteral(tree, pt) case tree: untpd.Inlined => typedInlined(tree, pt) @@ -1813,7 +1815,6 @@ class Typer extends Namer case tree: untpd.AndTypeTree => typedAndTypeTree(tree) case tree: untpd.OrTypeTree => typedOrTypeTree(tree) case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) - case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree) case tree: untpd.LambdaTypeTree => typedLambdaTypeTree(tree)(ctx.localContext(tree, NoSymbol).setNewScope) case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree) case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree, pt) @@ -1903,7 +1904,17 @@ class Typer extends Namer case none => typed(mdef) match { case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) => - buf += inlineExpansion(mdef1) + if (mdef1.symbol.isInlinedMethod) { + buf += inlineExpansion(mdef1) + // replace body with expansion, because it will be used as inlined body + // from separately compiled files - the original BodyAnnotation is not kept. + } + else { + assert(mdef1.symbol.isTransparentMethod, mdef.symbol) + Inliner.bodyToInline(mdef1.symbol) // just make sure accessors are computed, + buf += mdef1 // but keep original definition, since inline-expanded code + // is pickled in this case. + } case mdef1 => import untpd.modsDeco mdef match { @@ -1919,7 +1930,8 @@ class Typer extends Namer traverse(stats ++ rest) case stat :: rest => val stat1 = typed(stat)(ctx.exprContext(stat, exprOwner)) - if (!ctx.isAfterTyper && isPureExpr(stat1) && !stat1.tpe.isRef(defn.UnitClass)) + if (!ctx.isAfterTyper && isPureExpr(stat1) && + !stat1.tpe.isRef(defn.UnitClass) && !isSelfOrSuperConstrCall(stat1)) ctx.warning(em"a pure expression does nothing in statement position", stat.pos) buf += stat1 traverse(rest) @@ -2019,8 +2031,9 @@ class Typer extends Namer tryInsertImplicitOnQualifier(tree, pt, locked).getOrElse(fallBack) pt match { - case pt @ FunProto(Nil, _, _) - if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) && + case pt: FunProto + if pt.args.isEmpty && + tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) && tree.getAttachment(DroppedEmptyArgs).isEmpty => tree.putAttachment(DroppedEmptyArgs, ()) pt.markAsDropped() @@ -2083,15 +2096,13 @@ class Typer extends Namer * If all this fails, error * Parameters as for `typedUnadapted`. */ - def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ { - def showWithType(x: Any) = x match { - case tree: tpd.Tree @unchecked => i"$tree of type ${tree.tpe}" - case _ => String.valueOf(x) + def adapt(tree: Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = track("adapt") { + trace(i"adapting $tree to $pt", typr, show = true) { + adapt1(tree, pt, locked) } - adapt1(tree, pt, locked) } - def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + final def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { adapt(tree, pt, ctx.typerState.ownedVars) } @@ -2147,8 +2158,8 @@ class Typer extends Namer false } - def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { - case _: MethodOrPoly => + def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp.toLambda match { + case _: LambdaType => if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple) adapt(tree, pt.tupled, locked) else @@ -2221,7 +2232,6 @@ class Typer extends Namer } val args = eraseErasedArgs(implicitArgs(wtp.paramInfos)) - def propagatedFailure(args: List[Tree]): Type = args match { case arg :: args1 => arg.tpe match { @@ -2329,7 +2339,7 @@ class Typer extends Namer missingArgs(wtp) } - def adaptNoArgsOther(wtp: Type) = { + def adaptNoArgsOther(wtp: Type): Tree = { ctx.typeComparer.GADTused = false if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) && !untpd.isImplicitClosure(tree) && @@ -2343,16 +2353,13 @@ class Typer extends Namer checkEqualityEvidence(tree, pt) tree } + else if (Inliner.isInlineable(tree.symbol) && + (tree.symbol.isTransparentMethod || tree.tpe <:< pt)) + readaptSimplified(Inliner.inlineCall(tree, pt)) else if (tree.tpe <:< pt) { if (pt.hasAnnotation(defn.InlineParamAnnot)) checkInlineConformant(tree, isFinal = false, "argument to inline parameter") - if (Inliner.hasBodyToInline(tree.symbol) && - !ctx.owner.ownersIterator.exists(_.isInlineableMethod) && - !ctx.settings.YnoInline.value && - !ctx.isAfterTyper && - !ctx.reporter.hasErrors) - readaptSimplified(Inliner.inlineCall(tree, pt)) - else if (ctx.typeComparer.GADTused && pt.isValueType) + if (ctx.typeComparer.GADTused && pt.isValueType) // Insert an explicit cast, so that -Ycheck in later phases succeeds. // I suspect, but am not 100% sure that this might affect inferred types, // if the expected type is a supertype of the GADT bound. It would be good to come @@ -2384,7 +2391,7 @@ class Typer extends Namer def adaptNoArgs(wtp: Type): Tree = { val ptNorm = underlyingApplied(pt) lazy val functionExpected = defn.isFunctionType(ptNorm) - lazy val resultMatch = constrainResult(wtp, followAlias(pt)) + lazy val resultMatch = constrainResult(tree.symbol, wtp, followAlias(pt)) wtp match { case wtp: ExprType => readaptSimplified(tree.withType(wtp.resultType)) @@ -2480,8 +2487,9 @@ class Typer extends Namer } def adaptType(tp: Type): Tree = { + val isApplied = pt `eq` AnyTypeConstructorProto val tree1 = - if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree + if (isApplied || tp.typeParamSymbols.isEmpty) tree else { val tp1 = if (ctx.compilationUnit.isJava) @@ -2492,7 +2500,20 @@ class Typer extends Namer tree.tpe.EtaExpand(tp.typeParamSymbols) tree.withType(tp1) } - if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1 + val isCompatible = + if (ctx.mode `is` Mode.Pattern) true + else pt match { + case pt: TypeBounds if ctx.mode `is` Mode.Type => pt `contains` tree1.tpe + case _ => tree1.tpe <:< pt + } + if (!isApplied && Inliner.isInlineable(tree.symbol)) + readaptSimplified { + Inliner.inlineCall(tree, pt) match { + case inlined: Inlined => TypeTree(inlined.tpe).withPos(tree.pos) + case err => err + } + } + else if (isCompatible) tree1 else err.typeMismatch(tree1, pt) } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index d8327bc3a56b..bd801311acbe 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -163,7 +163,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """ |object Scope { | abstract class Concept - | new Concept() + | val x = new Concept() |} """.stripMargin } @@ -181,7 +181,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """ |object Scope { | trait Concept - | new Concept() + | val x = new Concept() |} """.stripMargin } @@ -508,7 +508,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """class Base |class RequiresBase { self: Base => } |object Scope { - | new RequiresBase + | val x = new RequiresBase |} |""".stripMargin } @@ -988,20 +988,6 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertEquals(method.show, "method foo") } - @Test def expectedTypeBoundOrEquals = - checkMessagesAfter(FrontEnd.name) { - """object typedef { - | type asd > Seq - |} - """.stripMargin - }.expect { (ictx, messages) => - implicit val ctx: Context = ictx - - assertMessageCount(1, messages) - val ExpectedTypeBoundOrEquals(found) :: Nil = messages - assertEquals(Tokens.IDENTIFIER, found) - } - @Test def classAndCompanionNameClash = checkMessagesAfter(RefChecks.name) { """ diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index f275cbcd497d..0d1fc54779b1 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -128,8 +128,9 @@ TypedFunParam ::= id ‘:’ Type InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds) WithType ::= AnnotType {‘with’ AnnotType} (deprecated) -AnnotType ::= SimpleType {Annotation} Annotated(t, annot) -SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args) +AnnotType ::= AppliedType {Annotation} Annotated(t, annot) +AppliedType ::= SimpleType {ParArgumentExprs} Apply(t, args) +SimpleType ::= SimpleType TypeArgs TypeApply(t, args) | SimpleType ‘#’ id Select(t, name) | StableId | [‘-’ | ‘+’ | ‘~’ | ‘!’] StableId PrefixOp(expr, op) @@ -150,6 +151,9 @@ NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’ Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds TypeBounds ::= [‘>:’ Type] [‘<:’ Type] | INT TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {‘<%’ Type} {‘:’ Type} ContextBounds(typeBounds, tps) + +TypeRHS ::= ‘if’ Expr ‘then’ TypeRHS ‘else’ TypeRHS If(cond, thenp, elsep) + | Type ``` ### Expressions @@ -309,8 +313,9 @@ ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig [‘:’ Type] DefDef(_, name, tparams, vparamss, tpe, EmptyTree) DefSig ::= id [DefTypeParamClause] DefParamClauses -TypeDcl ::= id [TypTypeParamClause] [‘=’ Type] TypeDefTree(_, name, tparams, tpt) - | id [HkTypeParamClause] TypeBounds TypeDefTree(_, name, tparams, bounds) +TypeDcl ::= id [TypTypeParamClause] [DefParamClause] TypeBounds DefDef(name, tparams, vparamss, bounds, rhs) + ‘=’ TypeRHS + | id [HkTypeParamClause] TypeBounds TypeDef(name, tparams, bounds) Def ::= ‘val’ PatDef | ‘var’ VarDef @@ -337,7 +342,7 @@ EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody] Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats) ConstrApps ::= ConstrApp {‘with’ ConstrApp} -ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) +ConstrApp ::= SimpleType {Annotation} {ParArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ConstrBlock SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} diff --git a/library/src/scala/tasty/Tasty.scala b/library/src/scala/tasty/Tasty.scala index 7b8d830f0d2c..704c8fd1b641 100644 --- a/library/src/scala/tasty/Tasty.scala +++ b/library/src/scala/tasty/Tasty.scala @@ -593,11 +593,13 @@ abstract class Tasty { tasty => type LambdaType[ParamInfo <: TypeOrBounds] <: Type type MethodType <: LambdaType[Type] type PolyType <: LambdaType[TypeBounds] + type TermLambda <: LambdaType[Type] type TypeLambda <: LambdaType[TypeBounds] implicit def typeClassTag: ClassTag[Type] implicit def methodTypeClassTag: ClassTag[MethodType] implicit def polyTypeClassTag: ClassTag[PolyType] + implicit def termLambdaClassTag: ClassTag[TermLambda] implicit def typeLambdaClassTag: ClassTag[TypeLambda] implicit def recursiveTypeClassTag: ClassTag[RecursiveType] @@ -617,6 +619,13 @@ abstract class Tasty { tasty => } implicit def PolyTypeDeco(tpt: PolyType): PolyTypeAPI + trait TermLambdaAPI { + def paramNames(implicit ctx: Context): List[String] + def paramTypes(implicit ctx: Context): List[Type] + def resultTpe(implicit ctx: Context): Type + } + implicit def TermLambdaDeco(tpt: TermLambda): TermLambdaAPI + trait TypeLambdaAPI { def paramNames(implicit ctx: Context): List[String] def paramTypes(implicit ctx: Context): List[TypeBounds] @@ -714,6 +723,11 @@ abstract class Tasty { tasty => def unapply(x: PolyType)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] } + val TermLambda: TermLambdaExtractor + abstract class TermLambdaExtractor { + def unapply(x: TermLambda)(implicit ctx: Context): Option[(List[String], List[Type], Type)] + } + val TypeLambda: TypeLambdaExtractor abstract class TypeLambdaExtractor { def unapply(x: TypeLambda)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] diff --git a/tests/neg-custom-args/fatal-warnings/i2333.scala b/tests/neg-custom-args/fatal-warnings/i2333.scala index a6772e77783e..a22433394346 100644 --- a/tests/neg-custom-args/fatal-warnings/i2333.scala +++ b/tests/neg-custom-args/fatal-warnings/i2333.scala @@ -1,4 +1,5 @@ @deprecated("bla", "2.11.0") class Foo { + println("") def this(x: Int) = this() } diff --git a/tests/neg/power.scala b/tests/neg/power.scala index 6230b4e51a21..b57812b957e3 100644 --- a/tests/neg/power.scala +++ b/tests/neg/power.scala @@ -5,11 +5,11 @@ object Test { if (n == 0) 1.0 else if (n == 1) x else { - val y = power(x, n / 2) // error: maximal number of inlines exceeded + val y = power(x, n / 2) if (n % 2 == 0) y * y else y * y * x } def main(args: Array[String]): Unit = { - println(power(2.0, args.length)) + println(power(2.0, args.length)) // error: maximal number of inlines exceeded } } diff --git a/tests/neg/typelevel.scala b/tests/neg/typelevel.scala new file mode 100644 index 000000000000..f75c425a4e9d --- /dev/null +++ b/tests/neg/typelevel.scala @@ -0,0 +1,58 @@ +trait HList { + def length: Int + def head: Any + def tail: HList + transparent def isEmpty: Boolean = + length == 0 +} + +// () +case object HNil extends HList { + transparent def length = 0 + def head: Nothing = ??? + def tail: Nothing = ??? +} + +// (H, T) +case class HCons[H, T <: HList](hd: H, tl: T) extends HList { + transparent def length = 1 + tl.length + def head: H = this.hd + def tail: T = this.tl +} + +object Test { + transparent def concat(xs: HList, ys: HList): HList = + if xs.isEmpty then ys + else HCons(xs.head, concat(xs.tail, ys)) + + class Deco(private val as: HList) { + transparent def ++ (bs: HList) = concat(as, bs) + } + + class Deco0(val as: HList) { + println("HI") + transparent def ++ (bs: HList) = concat(as, bs) + } + + class Eff { + println("HI") + } + class Deco1(val as: HList) extends Eff { + transparent def ++ (bs: HList) = concat(as, bs) + } + + // Test that selections from impure classes cannot be projected away + + val rr = new Deco(HCons(1, HNil)) ++ HNil + val rra: HCons[Int, HNil.type] = rr // ok + val rr2 = new Deco2(HCons(1, HNil)) ++ HNil + val rr2a: HCons[Int, HNil.type] = rr2 // ok + val rr0 = new Deco0(HCons(1, HNil)) ++ HNil + val rr0a: HCons[Int, HNil.type] = rr0 // error (type error because no inline) + val rr1 = new Deco1(HCons(1, HNil)) ++ HNil + val rr1a: HCons[Int, HNil.type] = rr1 // error (type error because no inline) + + class Deco2(val as: HList) extends java.lang.Cloneable with java.lang.Comparable[Deco2] { + transparent def ++ (bs: HList) = concat(as, bs) + } +} \ No newline at end of file diff --git a/tests/neg/typemethods.scala b/tests/neg/typemethods.scala new file mode 100644 index 000000000000..face996e196c --- /dev/null +++ b/tests/neg/typemethods.scala @@ -0,0 +1,25 @@ +trait Nat { + def toInt: Int +} + +case object Z extends Nat { + transparent def toInt = 0 +} + +case class S[N <: Nat](n: N) extends Nat { + transparent def toInt = n.toInt + 1 +} + +object Test extends App { + + type ToNat(n: Int) <: Nat = + if n == 0 then Z.type + else S[ToNat(n - 1)] + + type ToNat = Int // error: double definition + + type Foo[T](n: Int)(m: Int) = Int // error: cannot be curried + + type Bar(implicit m: Int) = Int // error: cannot be implicit + type Baz(erased m: Int) = Int // error: cannot be erased +} \ No newline at end of file diff --git a/tests/neg/typemethods2.scala b/tests/neg/typemethods2.scala new file mode 100644 index 000000000000..aa8aad6e0890 --- /dev/null +++ b/tests/neg/typemethods2.scala @@ -0,0 +1,28 @@ +trait Nat { + def toInt: Int +} + +case object Z extends Nat { + transparent def toInt = 0 +} + +case class S[N <: Nat](n: N) extends Nat { + transparent def toInt = n.toInt + 1 +} + +class C { + type ToNat = Int + + type ToNat2(n: Int) <: Nat = + if n == 0 then Z.type + else S[ToNat2(n - 1)]} + +object Test extends C { + + override type ToNat(n: Int) <: Nat = // error: illegal override + if n == 0 then Z.type + else S[ToNat(n - 1)] + + override type ToNat2[X] = X // error: illegal override + +} \ No newline at end of file diff --git a/tests/run/lst-transparent/Lst.scala b/tests/run/lst-transparent/Lst.scala new file mode 100644 index 000000000000..9e0f52076c0d --- /dev/null +++ b/tests/run/lst-transparent/Lst.scala @@ -0,0 +1,496 @@ +package dotty.tools.dotc +package util + +import printing.{Printer, Texts} +import Texts.Text +import collection.mutable.{ListBuffer, StringBuilder} + + +/** A lightweight class for lists, optimized for short and medium lengths. + * A list is represented at runtime as + * + * If it is empty: the value `Lst.Empty` + * If it contains one element: the element itself + * If it contains more elements: an Array[Any] containing the elements + */ +class Lst[+T](val elems: Any) extends AnyVal { + import Lst._ + + def length: Int = elems match { + case null => 0 + case elems: Arr => elems.length + case elem => 1 + } + + def isEmpty = elems == null + def nonEmpty = elems != null + + transparent def foreach(op: => T => Unit): Unit = { + def sharedOp(x: T) = op(x) + elems match { + case null => + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + while (i < elems.length) { sharedOp(elem(i)); i += 1 } + case elem: T @ unchecked => sharedOp(elem) + } + } + + /** Like `foreach`, but completely inlines `op`, at the price of generating the code twice. + * Should be used only of `op` is small + */ + transparent def foreachInlined(op: => T => Unit): Unit = elems match { + case null => + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + while (i < elems.length) { op(elem(i)); i += 1 } + case elem: T @unchecked => op(elem) + } + + def iterator(): Iterator[T] = elems match { + case null => Iterator.empty + case elems: Arr => elems.iterator.asInstanceOf[Iterator[T]] + case elem: T @unchecked => Iterator.single(elem) + } + + def copyToArray[U >: T](target: Array[U], from: Int) = elems match { + case null => + case elems: Arr => System.arraycopy(elems, 0, target, from, elems.length) + case elem: T @ unchecked => target(from) = elem + } + + /** `f` is pulled out, not duplicated */ + transparent def map[U](f: => T => U): Lst[U] = { + def op(x: T) = f(x) + elems match { + case null => Empty + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + val newElems = new Arr(elems.length) + var i = 0 + while (i < elems.length) { newElems(i) = op(elem(i)); i += 1 } + new Lst[U](newElems) + case elem: T @ unchecked => new Lst[U](op(elem)) + } + } + + def mapConserve[U](f: T => U): Lst[U] = elems match { + case null => Empty + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var newElems: Arr = null + var i = 0 + while (i < elems.length) { + val x = elem(i) + val y = f(x) + if (newElems != null) newElems(i) = y + else if (!eq(x, y)) { + newElems = new Arr(elems.length) + System.arraycopy(elems, 0, newElems, 0, i) + newElems(i) = y + } + i += 1 + } + if (newElems == null) this.asInstanceOf[Lst[U]] else new Lst[U](newElems) + case elem: T @ unchecked => new Lst[U](f(elem)) + } + + def flatMap[U](f: T => Lst[U]): Lst[U] = elems match { + case null => Empty + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + val newElemss: Arr = new Arr(elems.length) + var i = 0 + var len = 0 + while (i < elems.length) { + val ys = f(elem(i)) + len += ys.length + newElemss(i) = ys.elems + i += 1 + } + if (len == 0) Empty + else if (len == 1) { + i = 0 + while (newElemss(i) == null) i += 1 + new Lst[U](newElemss(i)) + } + else { + val newElems = new Arr(len) + i = 0 + var j = 0 + while (i < newElemss.length) { + val ys = new Lst[U](newElemss(i)) + ys.copyToArray(newElems, j) + j += ys.length + i += 1 + } + new Lst[U](newElems) + } + case elem: T @ unchecked => new Lst[U](f(elem).elems) + } + + def filter(p: T => Boolean): Lst[T] = elems match { + case null => Empty + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + val scratch = new Arr(elems.length) + var i = 0 + var len = 0 + while (i < elems.length) { + val x = elem(i) + if (p(x)) { scratch(len) = x; len += 1 } + i += 1 + } + if (len == elems.length) this + else _fromArray(scratch, 0, len) + case elem: T @unchecked => + if (p(elem)) this else Empty + } + def filterNot(p: T => Boolean): Lst[T] = filter(!p(_)) + + transparent def exists(p: => T => Boolean): Boolean = { + def op(x: T) = p(x) + elems match { + case null => false + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + while (i < elems.length && !op(elem(i))) i += 1 + i < elems.length + case elem: T @unchecked => + op(elem) + } + } + + transparent def forall(p: => T => Boolean): Boolean = { + def op(x: T) = p(x) + elems match { + case null => true + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + while (i < elems.length && op(elem(i))) i += 1 + i == elems.length + case elem: T @unchecked => + op(elem) + } + } + + transparent def contains[U >: T](x: U): Boolean = elems match { + case null => false + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + while (i < elems.length && elem(i) != x) i += 1 + i < elems.length + case elem: T @unchecked => + elem == x + } + + transparent def foldLeft[U](z: U)(f: => (U, T) => U) = { + def op(x: U, y: T) = f(x, y) + elems match { + case null => z + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 0 + var acc = z + while (i < elems.length) { acc = op(acc, elem(i)); i += 1 } + acc + case elem: T @unchecked => + op(z, elem) + } + } + + transparent def /: [U](z: U)(op: => (U, T) => U) = foldLeft(z)(op) + + def reduceLeft[U >: T](op: (U, U) => U) = elems match { + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + var i = 1 + var acc: U = elem(0) + while (i < elems.length) { acc = op(acc, elem(i)); i += 1 } + acc + case elem: T @unchecked => + elem + } + + def reverse: Lst[T] = elems match { + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + val newElems = new Arr(elems.length) + var i = 0 + while (i < elems.length) { + newElems(elems.length - 1 - i) = elem(i) + i += 1 + } + new Lst[T](newElems) + case _ => this + } + + def apply(n: Int): T = elems match { + case null => throw new IndexOutOfBoundsException(n.toString) + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + elem(n) + case elem: T @unchecked => + if (n == 0) elem else throw new IndexOutOfBoundsException(n.toString) + } + + def head: T = apply(0) + def last: T = apply(length - 1) + + def headOr[U >: T](alt: => U): U = if (isEmpty) alt else head + + def slice(start: Int, end: Int): Lst[T] = + if (start < 0) slice(0, end) + else elems match { + case null => this + case elems: Arr => _fromArray(elems, start, end `min` elems.length) + case elem: T @ unchecked => if (end == 0) Empty else this + } + + def drop(n: Int): Lst[T] = slice(n, length) + def tail = drop(1) + def take(n: Int): Lst[T] = slice(0, n) + + def ++ [U >: T](that: Lst[U]): Lst[U] = + if (elems == null) that + else if (that.elems == null) this + else { + val len1 = this.length + val len2 = that.length + val newElems = new Arr(len1 + len2) + this.copyToArray(newElems, 0) + that.copyToArray(newElems, len1) + new Lst[U](newElems) + } + + def zipWith[U, V](that: Lst[U])(op: (T, U) => V): Lst[V] = + this.elems match { + case null => Empty + case elems1: Arr => def elem1(i: Int) = elems1(i).asInstanceOf[T] + that.elems match { + case null => Empty + case elems2: Arr => def elem2(i: Int) = elems2(i).asInstanceOf[U] + val len = elems1.length min elems2.length + if (len == 0) Empty + else if (len == 1) new Lst[V](op(elem1(0), elem2(0))) + else { + var newElems: Arr = null + var i = 0 + while (i < len) { + val x = elem1(i) + val y = op(x, elem2(i)) + if (newElems != null) newElems(i) = y + else if (!eq(x, y)) { + newElems = new Arr(len) + System.arraycopy(elems, 0, newElems, 0, i) + newElems(i) = y + } + i += 1 + } + new Lst[V](newElems) + } + case elem2: U @unchecked => + new Lst[V](op(elem1(0), elem2)) + } + case elem1: T @unchecked => + that.elems match { + case null => Empty + case elems2: Arr => def elem2(i: Int) = elems2(i).asInstanceOf[U] + new Lst[V](op(elem1, elem2(0))) + case elem2: U @unchecked => new Lst[V](op(elem1, elem2)) + } + } + + def zip[U](that: Lst[U]): Lst[(T, U)] = zipWith(that)((_, _)) + + def zipWithIndex: Lst[(T, Int)] = elems match { + case null => Empty + case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T] + val newElems = new Arr(elems.length) + var i = 0 + while (i < elems.length) { newElems(i) = (elem(i), i); i += 1 } + new Lst[(T, Int)](newElems) + case elem: T @unchecked => + new Lst[(T, Int)]((elem, 0)) + } + + def corresponds[U](that: Lst[U])(p: (T, U) => Boolean): Boolean = + (this `eqLst` that) || { + this.elems match { + case null => + that.elems == null + case elems1: Arr => def elem1(i: Int) = elems1(i).asInstanceOf[T] + that.elems match { + case elems2: Arr => def elem2(i: Int) = elems2(i).asInstanceOf[U] + val len = elems1.length + len == elems2.length && { + var i = 0 + while (i < len && p(elem1(i), elem2(i))) i += 1 + i == len + } + case _ => false + } + case elem1: T @unchecked => + that.elems match { + case null => false + case elems2: Arr => false + case elem2: U @unchecked => p(elem1, elem2) + } + } + } + + def === [U](that: Lst[U]) = + (this `eqLst` that) || { + elems match { + case elems1: Arr => + that.elems match { + case elems2: Arr => + val len = elems1.length + len == elems2.length && { + var i = 0 + while (i < len && elems1(i).equals(elems2(i))) i += 1 + i == len + } + case _ => false + } + case elem => elem == that.elems + } + } + + def eqLst[U](that: Lst[U]) = eq(elems, that.elems) + + def eqElements[U](that: Lst[U]): Boolean = corresponds(that)(eqFn) + + def mkString: String = mkString(", ") + + def mkString(sep: String): String = mkString("", sep, "") + def mkString(left: String, sep: String, right: String): String = { + val b = new StringBuilder(left) + var first = true + foreachInlined { elem => + if (first) first = false else b ++= sep + b ++= elem.toString + } + b ++= right + b.toString + } + + override def toString = mkString("Lst(", ", ", ")") +} + +object Lst { + + private type Arr = Array[Any] + + private def eq(x: Any, y: Any) = x.asInstanceOf[AnyRef] `eq` y.asInstanceOf[AnyRef] + private val eqFn = (x: Any, y: Any) => eq(x, y) + + val Empty = new Lst[Nothing](null) + + def apply[T](): Lst[T] = Empty + + def apply[T](x0: T): Lst[T] = new Lst[T](x0) + + def apply[T](x0: T, x1: T): Lst[T] = { + val elems = new Arr(2) + elems(0) = x0 + elems(1) = x1 + new Lst[T](elems) + } + + def apply[T](x0: T, x1: T, x2: T): Lst[T] = { + val elems = new Arr(3) + elems(0) = x0 + elems(1) = x1 + elems(2) = x2 + new Lst[T](elems) + } + + def apply[T](x0: T, x1: T, x2: T, x3: T): Lst[T] = { + val elems = new Arr(4) + elems(0) = x0 + elems(1) = x1 + elems(2) = x2 + elems(3) = x3 + new Lst[T](elems) + } + + def apply[T](x0: T, x1: T, x2: T, x3: T, x4: T, xs: T*): Lst[T] = { + val elems = new Arr(5 + xs.length) + elems(0) = x0 + elems(1) = x1 + elems(2) = x2 + elems(3) = x3 + elems(4) = x4 + xs.copyToArray(elems, 5) + new Lst[T](elems) + } + + def fill[T](n: Int)(elem: => T) = { + val elems = new Arr(n) + var i = 0 + while (i < n) { elems(i) = elem; i += 1} + new Lst[T](elems) + } + + class Buffer[T] { + private var len = 0 + private var elem: T = _ + private var elems: Arr = _ + + def size = len + + /** pre: len > 0, n > 1 */ + private def ensureSize(n: Int) = + if (len == 1) { + elems = new Arr(n `max` 16) + elems(0) = elem + } + else if (len < n) { + val newLen = n `max` len << 2 + val newElems = new Arr(newLen) + System.arraycopy(elems, 0, newElems, 0, len) + elems = newElems + } + + def += (x: T): this.type = { + if (len == 0) elem = x + else { + ensureSize(len + 1) + elems(len) = x + } + len += 1 + this + } + + def ++= (xs: Lst[T]): this.type = { + xs.elems match { + case null => this + case elems2: Arr => + if (len == 0) elems = elems2 + else { + ensureSize(len + elems2.length) + System.arraycopy(elems2, 0, elems, len, elems2.length) + } + len += elems2.length + case elem: T @unchecked => this += elem + } + this + } + + def toLst: Lst[T] = + if (len == 0) Empty + else if (len == 1) new Lst[T](elem) + else _fromArray(elems, 0, len) + + def clear() = + len = 0 + } + + private def _fromArray[T](elems: Arr, start: Int, end: Int): Lst[T] = { + val len = end - start + if (len <= 0) Empty + else if (len == 1) new Lst[T](elems(start)) + else if (start == 0 && end == elems.length) new Lst[T](elems) + else { + val newElems = new Arr(len) + System.arraycopy(elems, start, newElems, 0, len) + new Lst[T](newElems) + } + } + + def fromArray[T](elems: Array[T], start: Int, end: Int): Lst[T] = + _fromArray(elems.asInstanceOf[Arr], start, end) +} diff --git a/tests/run/lst-transparent/LstTest.scala b/tests/run/lst-transparent/LstTest.scala new file mode 100644 index 000000000000..87f2541fb62a --- /dev/null +++ b/tests/run/lst-transparent/LstTest.scala @@ -0,0 +1,329 @@ +object Test extends App { + import dotty.tools.dotc.util.Lst + + val xs0: Lst[String] = Lst.Empty + val xs1 = Lst("a") + val xs2 = Lst("a", "b") + val xs3 = Lst("a", "b", "c") + val xs4 = Lst("a", "b", "c", "d") + val xs5 = Lst("a", "b", "c", "d", "e") + val xs10 = xs5 ++ xs5 + + val is0: Lst[Int] = Lst.Empty + val is1 = Lst(1) + val is2 = Lst(1, 2) + val is3 = Lst(1, 2, 3) + val is4 = Lst(1, 2, 3, 4) + val is5 = Lst(1, 2, 3, 4, 5) + val is10 = is5 ++ is5 + + def lengthTest() = { + assert(xs0.length == 0) + assert(xs1.length == 1) + assert(xs2.length == 2) + assert(xs10.length == 10) + + assert(is0.length == 0) + assert(is1.length == 1) + assert(is2.length == 2) + assert(is10.length == 10) + } + + def concatTest() = { + assert(xs1 ++ xs0 == xs1) + assert(xs0 ++ xs1 == xs1) + assert(xs3 ++ xs0 == xs3) + assert(xs0 ++ xs4 == xs4) + + assert(is1 ++ is0 == is1) + assert(is0 ++ is1 == is1) + assert(is3 ++ is0 == is3) + assert(is0 ++ is4 == is4) + } + + def foreachTest() = { + xs0.foreach(s => assert(s.length == 1)) + xs3.foreach(s => assert(s.length == 1)) + def test1() = { + var x = 0 + xs10.foreach(elem => x += elem.length) + } + def test2() = { + var y = 0 + xs10.foreachInlined(elem => y += elem.length) + } + test1() + test2() + + is0.foreach(i => assert(i == 1)) + is3.foreach(i => assert(i <= 3)) + } + + def mapTest() = { + val ys0 = xs0.map(_.reverse) + val ys1 = xs1.map(s => s + s) + assert(ys1.mkString == "aa") + val ys5 = xs5.map(s => s + s) + assert(ys5.mkString == "aa, bb, cc, dd, ee") + + val js0 = is0.map(i => i * i) + val js1 = is1.map(i => i + i) + assert(js1.mkString == "2") + val js5 = is5.map(s => s + s) + assert(js5.mkString == "2, 4, 6, 8, 10") + } + + def mapConserveTest() = { + val ys0 = xs0.mapConserve(_.reverse) + val ys1 = xs1.mapConserve(s => s + s) + assert(ys1.mkString == "aa") + val ys2 = xs2.mapConserve(identity) + assert(ys2 `eqLst` xs2) + val ys5 = xs5.map(s => s + s) + assert(ys5.mkString == "aa, bb, cc, dd, ee") + val ys4 = xs4.mapConserve(s => if (s == "c") "cc" else s) + assert(ys4.mkString == "a, b, cc, d") + + val js0 = is0.mapConserve(i => i * i) + val js1 = is1.mapConserve(s => s + s) + assert(js1.mkString == "2") + val js2 = is2.mapConserve(identity) + assert(js2 `eqLst` is2) + val js5 = is5.map(s => s + s) + assert(js5.mkString == "2, 4, 6, 8, 10") + val js4 = is4.mapConserve(s => if (s == 3) -3 else s) + assert(js4.mkString == "1, 2, -3, 4") + } + + def flatMapTest() = { + val ys0 = xs0.flatMap(s => Lst(s, s)) + assert(ys0.isEmpty) + val ys2 = xs2.flatMap(s => Lst(s, s)) + assert(ys2.mkString == "a, a, b, b") + val ys2a = xs2.flatMap(_ => Lst.Empty) + assert(ys2a.isEmpty) + val ys4 = xs4.flatMap(s => Lst.fill(s.head - 'a')(s)) + assert(ys4.mkString == "b, c, c, d, d, d") + val ys5 = xs5.flatMap(s => if s == "c" then Lst(s) else Lst()) + assert(ys5 == Lst("c")) + + val js0 = is0.flatMap(s => Lst(s, s)) + assert(js0.isEmpty) + val js2 = is2.flatMap(s => Lst(s, s)) + assert(js2.mkString == "1, 1, 2, 2") + val js2a = is2.flatMap(_ => Lst.Empty) + assert(js2a.isEmpty) + val js4 = is4.flatMap(s => Lst.fill(s - 1)(s)) + assert(js4.mkString == "2, 3, 3, 4, 4, 4", js4) + val js5 = is5.flatMap(s => if s == 3 then Lst(-3) else Lst()) + assert(js5 == Lst(-3)) + } + + def filterTest() = { + val ys0 = xs0.filter(_.head >= 'c') + assert(ys0.isEmpty) + val ys1 = xs1.filter(_.head >= 'c') + assert(ys1.isEmpty) + val ys1a = xs1.filterNot(_.head >= 'c') + assert(ys1a `eqLst` xs1) + val ys5 = xs5.filter(_.head % 2 != 0) + assert(ys5 === Lst("a", "c", "e"), ys5) + + val js0 = is0.filter(_ > 3) + assert(js0.isEmpty) + val js1 = is1.filter(_ > 3) + assert(js1.isEmpty) + val js1a = is1.filterNot(_ > 3) + assert(js1a `eqLst` is1) + val js5 = is5.filter(_ % 2 != 0) + assert(js5 === Lst(1, 3, 5), js5) + } + + def existsTest() = { + assert(!xs0.exists(_ => true)) + assert(xs1.exists(_ == "a")) + assert(xs5.exists(_ == "c")) + assert(!xs5.exists(_.head > 'e')) + + assert(!is0.exists(_ => true)) + assert(is1.exists(_ == 1)) + assert(is5.exists(_ == 3)) + assert(!is5.exists(_ > 5)) + } + + def forallTest() = { + assert(xs0.forall(_ => false)) + assert(xs1.forall(_ == "a")) + assert(xs5.forall(_.head <= 'e')) + assert(!xs5.forall(_ == "c")) + } + + def containsTest() = { + assert(!xs0.contains("")) + assert(xs1.contains("a")) + assert(xs10.contains("e")) + assert(!xs10.contains("f")) + + assert(!is0.contains(2)) + assert(is1.contains(1)) + assert(is10.contains(5)) + assert(!is10.contains(6)) + } + + def foldTest() = { + assert(xs0.foldLeft("x")(_ ++ _) == "x") + assert(xs1.foldLeft("x")(_ ++ _) == "xa") + assert(xs2.foldLeft("x")(_ ++ _) == "xab") + assert(xs3.foldLeft("x")(_ ++ _) == "xabc") + assert(("x" /: xs0)(_ ++ _) == "x") + assert(("x" /: xs1)(_ ++ _) == "xa") + assert(("x" /: xs2)(_ ++ _) == "xab") + assert(("x" /: xs3)(_ ++ _) == "xabc") + assert(xs1.reduceLeft(_ + _) == "a") + assert(xs3.reduceLeft(_ + _) == "abc") + + assert(is0.foldLeft(3)(_ + _) == 3) + assert(is1.foldLeft(3)(_ + _) == 4) + assert(is2.foldLeft(3)(_ + _) == 6) + assert(is3.foldLeft(3)(_ + _) == 9) + assert((3 /: is0)(_ + _) == 3) + assert((3 /: is1)(_ + _) == 4) + assert((3 /: is2)(_ + _) == 6) + assert((3 /: is3)(_ + _) == 9) + assert(is1.reduceLeft(_ + _) == 1) + assert(is3.reduceLeft(_ + _) == 6) + } + + def reverseTest() = { + assert(xs0.reverse === xs0) + assert(xs1.reverse === xs1) + assert(xs3.reverse.mkString == "c, b, a", xs3.reverse.mkString) + assert(xs4.reverse.reverse === xs4, xs4.reverse.reverse) + } + + def applyTest() = { + assert(xs5.head == "a") + assert(xs5.last == "e") + assert(xs5(3) == "d") + assert(xs1(0) == "a") + + assert(is5.head == 1) + assert(is5.last == 5) + assert(is5(3) == 4) + assert(is1(0) == 1) + } + + def sliceTest() = { + assert(xs5.slice(2, 4) === Lst("c", "d")) + assert(xs5.drop(4) === Lst("e")) + assert(xs5.take(4).mkString("") == "abcd") + assert(xs5.drop(-1) `eqLst` xs5) + assert(xs1.take(1) `eqLst` xs1) + assert(xs0.take(10).length == 0) + + assert(is5.slice(2, 4) === Lst(3, 4)) + assert(is5.drop(4) === Lst(5)) + assert(is5.take(4).mkString("") == "1234") + assert(is5.drop(-1) `eqLst` is5) + assert(is1.take(1) `eqLst` is1) + assert(is0.take(10).length == 0) + } + + def zipWithTest() = { + val ys4a = xs4.zipWith(xs5)(_ + _) + val ys4b = xs5.zipWith(xs4)(_ + _) + assert(ys4a.mkString("") == "aabbccdd", ys4a) + assert(ys4a === ys4b) + val ys1a = xs1.zipWith(xs1)(_ + _) + assert(ys1a === Lst("aa")) + val ys1b = xs1.zipWith(xs2)(_ + _) + assert(ys1b === Lst("aa")) + val ys1c = xs2.zipWith(xs1)(_ + _) + assert(ys1c === Lst("aa")) + val ys0a = xs1.zipWith(xs0)(_ + _) + val ys0b = xs0.zipWith(xs1)(_ + _) + assert((ys0a ++ ys0b).isEmpty) + val ys3i = xs3.zipWithIndex.map((x, y) => (x, y + 1)) + assert(ys3i === Lst(("a", 1), ("b", 2), ("c", 3)), ys3i) + + val js4a = is4.zipWith(is5)(_ + _) + val js4b = is5.zipWith(is4)(_ + _) + assert(js4a.mkString("") == "2468", js4a) + assert(js4a === js4b) + val js1a = is1.zipWith(is1)(_ + _) + assert(js1a === Lst(2)) + val js1b = is1.zipWith(is2)(_ + _) + assert(js1b === Lst(2)) + val js1c = is2.zipWith(is1)(_ + _) + assert(js1c === Lst(2)) + val js0a = is1.zipWith(is0)(_ + _) + val js0b = is0.zipWith(is1)(_ + _) + assert((js0a ++ js0b).isEmpty) + val js3i = is3.zipWithIndex.map((x, y) => (x, y + 1)) + assert(js3i === Lst((1, 1), (2, 2), (3, 3)), js3i) + assert(js3i.forall(_ == _)) + } + + def correspondsTest() = { + assert(xs4.corresponds(xs4)(_ == _)) + assert(!xs4.corresponds(xs5)(_ == _)) + assert(xs1.corresponds(xs1)(_ == _)) + assert(!xs1.corresponds(Lst("b"))(_ == _)) + assert(xs0.corresponds(xs0)(_ == _)) + assert(!xs0.corresponds(xs1)(_ == _)) + val zs1 = Lst(new Object, new Object) + val zs2 = Lst(zs1(0), zs1(1)) + val zs3 = Lst(new Object, new Object) + assert(zs1.eqElements(zs1)) + assert(zs1.eqElements(zs2)) + assert(!zs1.eqElements(zs3)) + + assert(is4.corresponds(is4)(_ == _)) + assert(!is4.corresponds(is5)(_ == _)) + assert(is1.corresponds(is1)(_ == _)) + assert(!is1.corresponds(Lst(-1))(_ == _)) + assert(is0.corresponds(is0)(_ == _)) + assert(!is0.corresponds(is1)(_ == _)) + } + + def bufferTest() = { + { val b = new Lst.Buffer[String] + b += "a" + assert(b.size == 1) + assert(b.toLst === Lst("a")) + b += "aa" + b ++= Lst.fill(20)("a") + assert(b.toLst.mkString("") == "a" * 23) + assert(b.size == 22) + } + + { val b = new Lst.Buffer[Int] + b += 1 + assert(b.size == 1) + assert(b.toLst === Lst(1)) + b += 11 + b ++= Lst.fill(20)(1) + assert(b.toLst.mkString("") == "1" * 23) + assert(b.size == 22) + } + } + + println("testing") + lengthTest() + concatTest() + foreachTest() + mapTest() + mapConserveTest() + flatMapTest() + filterTest() + existsTest() + forallTest() + containsTest() + foldTest() + reverseTest() + applyTest() + sliceTest() + zipWithTest() + correspondsTest() + bufferTest() +} \ No newline at end of file diff --git a/tests/run/reduce-projections.check b/tests/run/reduce-projections.check new file mode 100644 index 000000000000..b09296cec78b --- /dev/null +++ b/tests/run/reduce-projections.check @@ -0,0 +1,59 @@ +1 +2 +3 +4 +1 +1 +2 +3 +4 +2 +1 +2 +3 +4 +3 +1 +2 +3 +4 +4 +=== +2 +3 +4 +1 +1 +3 +4 +2 +1 +2 +4 +3 +1 +2 +3 +4 +=== +2 +3 +1 +3 +2 +2 +3 +2 +3 +4 +=== +2 +3 +1 +3 +2 +2 +3 +2 +3 +4 diff --git a/tests/run/reduce-projections.scala b/tests/run/reduce-projections.scala new file mode 100644 index 000000000000..fc4099100404 --- /dev/null +++ b/tests/run/reduce-projections.scala @@ -0,0 +1,122 @@ + +class C(val x1: Int, val x2: Int, val x3: Int, val x4: Int) + +object Test { + + class D(n: Int) { + println(n) + def result = n + } + object O2 extends D(2) + object O2a extends D(2) + object O2b extends D(2) + object O3 extends D(3) + object O3a extends D(3) + object O3b extends D(3) + + transparent def f(): Unit = { + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x1) + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x2) + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x3) + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x4) + println("===") + println(new C( + { 1 }, + { println(2); 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x1) + println(new C( + { println(1); 1 }, + { 2 }, + { println(3); 3 }, + { println(4); 4 } + ).x2) + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { 3 }, + { println(4); 4 } + ).x3) + println(new C( + { println(1); 1 }, + { println(2); 2 }, + { println(3); 3 }, + { 4 } + ).x4) + println("===") + println(new C( + { 1 }, + { println(2); 2 }, + { println(3); 3 }, + { 4 } + ).x1) + println(new C( + { 1 }, + { 2 }, + { println(3); 3 }, + { 4 } + ).x2) + println(new C( + { 1 }, + { println(2); 2 }, + { 3 }, + { 4 } + ).x3) + println(new C( + { 1 }, + { println(2); 2 }, + { println(3); 3 }, + { 4 } + ).x4) + println("===") + println(new C( + { 1 }, + O2.result, + O3.result, + { 4 } + ).x1) + println(new C( + { 1 }, + { 2 }, + O3a.result, + { 4 } + ).x2) + println(new C( + { 1 }, + O2a.result, + { 3 }, + { 4 } + ).x3) + println(new C( + { 1 }, + O2b.result, + O3b.result, + { 4 } + ).x4) + } + + def main(args: Array[String]): Unit = { + f() + } +} \ No newline at end of file diff --git a/tests/run/transparent-implicits.check b/tests/run/transparent-implicits.check new file mode 100644 index 000000000000..17a1d370d730 --- /dev/null +++ b/tests/run/transparent-implicits.check @@ -0,0 +1 @@ +(X(),Y()) diff --git a/tests/run/transparent-implicits.scala b/tests/run/transparent-implicits.scala new file mode 100644 index 000000000000..bd41c41eecb9 --- /dev/null +++ b/tests/run/transparent-implicits.scala @@ -0,0 +1,26 @@ +case class X() +case class Y() + +object impl { + implicit val y: Y = new Y() +} + +object inlines { + import impl._ + + class C { + implicit val x: X = new X() + + transparent + def f(): (X, Y) = + (implicitly[X], implicitly[Y]) + } +} + +object Test { + def main(args: Array[String]) = { + val c = new inlines.C + val xy = c.f() + println(xy) + } +} diff --git a/tests/run/transparent-object.check b/tests/run/transparent-object.check new file mode 100644 index 000000000000..5716ca5987cb --- /dev/null +++ b/tests/run/transparent-object.check @@ -0,0 +1 @@ +bar diff --git a/tests/run/transparent-object.scala b/tests/run/transparent-object.scala new file mode 100644 index 000000000000..21d2566022df --- /dev/null +++ b/tests/run/transparent-object.scala @@ -0,0 +1,14 @@ + +object Test { + def main(args: Array[String]): Unit = { + Foo.foo + } +} + +object Foo extends Bar { + transparent def foo: Unit = bar +} + +class Bar { + def bar: Unit = println("bar") +} diff --git a/tests/run/transparent.check b/tests/run/transparent.check new file mode 100644 index 000000000000..5f711274b935 --- /dev/null +++ b/tests/run/transparent.check @@ -0,0 +1,9 @@ +100 +10000 + + Inner +Outer.f +Outer.f Inner + Inner +Outer.f +Outer.f Inner diff --git a/tests/run/transparent/Test_2.scala b/tests/run/transparent/Test_2.scala new file mode 100644 index 000000000000..a2ab2caca22f --- /dev/null +++ b/tests/run/transparent/Test_2.scala @@ -0,0 +1,21 @@ +object Test { + + import p.transparents._ + + def main(args: Array[String]): Unit = { + println(f(10)) + println(f(f(10))) + + track("hello") { println("") } + + val o = new Outer + val i = new o.Inner + println(i.m) + println(i.g) + println(i.h) + println(o.inner.m) + println(o.inner.g) + println(o.inner.h) + } + +} diff --git a/tests/run/transparent/inlines_1.scala b/tests/run/transparent/inlines_1.scala new file mode 100644 index 000000000000..946599b1a196 --- /dev/null +++ b/tests/run/transparent/inlines_1.scala @@ -0,0 +1,41 @@ +package p +import collection.mutable + +object transparents { + + final val monitored = false + + transparent def f(x: Int): Int = x * x + + val hits = new mutable.HashMap[String, Int] { + override def default(key: String): Int = 0 + } + + def record(fn: String, n: Int = 1) = { + if (monitored) { + val name = if (fn.startsWith("member-")) "member" else fn + hits(name) += n + } + } + + @volatile private var stack: List[String] = Nil + + transparent def track[T](fn: String)(op: => T) = + if (monitored) { + stack = fn :: stack + record(fn) + try op + finally stack = stack.tail + } else op + + class Outer { + def f = "Outer.f" + class Inner { + val msg = " Inner" + transparent def m = msg + transparent def g = f + transparent def h = f ++ m + } + val inner = new Inner + } +} diff --git a/tests/run/transparentAccess/C_1.scala b/tests/run/transparentAccess/C_1.scala new file mode 100644 index 000000000000..45db8603e8c6 --- /dev/null +++ b/tests/run/transparentAccess/C_1.scala @@ -0,0 +1,7 @@ +package p { +class C { + protected def f(): Unit = () + + transparent def inl() = f() // error (when inlined): not accessible +} +} diff --git a/tests/run/transparentAccess/Test_2.scala b/tests/run/transparentAccess/Test_2.scala new file mode 100644 index 000000000000..98ea7693abb7 --- /dev/null +++ b/tests/run/transparentAccess/Test_2.scala @@ -0,0 +1,7 @@ + +object Test { + def main(args: Array[String]) = { + val c = new p.C() + c.inl() + } +} diff --git a/tests/run/transparentArrowAssoc.scala b/tests/run/transparentArrowAssoc.scala new file mode 100644 index 000000000000..80dd0d90d3c3 --- /dev/null +++ b/tests/run/transparentArrowAssoc.scala @@ -0,0 +1,24 @@ +import scala.collection.immutable._ + +import scala.collection.mutable.{ Builder, ListBuffer } + +object Test { + + private val defaultOrdering = Map[Numeric[_], Ordering[_]]( + Numeric.BigIntIsIntegral -> Ordering.BigInt, + Numeric.IntIsIntegral -> Ordering.Int + ) + + final implicit class ArrowAssoc[A](private val self: A) extends AnyVal { + transparent def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y) + def →[B](y: B): Tuple2[A, B] = ->(y) + } + + def main(args: Array[String]): Unit = { + assert((1 -> 2) == (1, 2)) + assert((1 → 2) == (1, 2)) + } + + +} + diff --git a/tests/run/transparentAssign.scala b/tests/run/transparentAssign.scala new file mode 100644 index 000000000000..4fec5fe255be --- /dev/null +++ b/tests/run/transparentAssign.scala @@ -0,0 +1,24 @@ +object Test { + + transparent def swap[T](x: T, x_= : => T => Unit, y: T, y_= : => T => Unit) = { + x_=(y) + y_=(x) + } + + transparent def f(x: Int => Unit) = x + + def main(args: Array[String]) = { + var x = 1 + var y = 2 + transparent def setX(z: Int) = x = z + transparent def setY(z: Int) = y = z + swap(x, setX, y, setY) + assert(x == 2 && y == 1) + + swap(x, x = _, y, y = _) + assert(x == 1 && y == 2) + + + val z = f(setX) // tests case where inline arg is not applied + } +} diff --git a/tests/run/transparentByName.scala b/tests/run/transparentByName.scala new file mode 100644 index 000000000000..b45451d4feda --- /dev/null +++ b/tests/run/transparentByName.scala @@ -0,0 +1,37 @@ +object Test { + + class Range(from: Int, end: Int) { + transparent def foreach(op: => Int => Unit): Unit = { + var i = from + while (i < end) { + op(i) + i += 1 + } + } + } + transparent def twice(op: => Int => Unit): Unit = { + op(1) + op(2) + } + transparent def thrice(op: => Unit): Unit = { + op + op + op + } + + def main(args: Array[String]) = { + var j = 0 + new Range(1, 10).foreach(j += _) + assert(j == 45, j) + twice { x => j = j - x } + thrice { j = j + 1 } + val f = new Range(1, 10).foreach + f(j -= _) + assert(j == 0, j) + new Range(1, 10).foreach { i1 => + new Range(2, 11).foreach { i2 => + j += i1 * i2 + } + } + } +} diff --git a/tests/run/transparentForeach.check b/tests/run/transparentForeach.check new file mode 100644 index 000000000000..3fced2fad78d --- /dev/null +++ b/tests/run/transparentForeach.check @@ -0,0 +1,137 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +1 +2 +3 +4 +5 +6 +7 +8 +9 +1 +2 +3 +4 +5 +6 +7 +8 +9 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +6 +6 +6 +6 +6 +6 +6 +6 +6 +6 +7 +7 +7 +7 +7 +7 +7 +7 +7 +7 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +9 +9 +9 +9 +9 +9 +9 +9 +9 +9 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 diff --git a/tests/run/transparentForeach.scala b/tests/run/transparentForeach.scala new file mode 100644 index 000000000000..eda163707093 --- /dev/null +++ b/tests/run/transparentForeach.scala @@ -0,0 +1,67 @@ +object Test { + + class Range(from: Int, end: Int) { + + transparent + def foreach(op: => Int => Unit): Unit = { + var i = from + while (i < end) { + op(i) + i += 1 + } + } + + def filter(p: Int => Boolean): List[Int] = ??? + } + + implicit class intWrapper(private val start: Int) extends AnyVal { + def until(end: Int) = new Range(start, end) + def to(limit: Int) = new Range(start, limit + 1) + } + + def matmul(xs: Array[Array[Double]], ys: Array[Array[Double]]): Array[Array[Double]] = { + def nrows = xs.length + def ncols = ys(0).length + def n = ys.length + assert(xs(0).length == n) + val zs = Array.ofDim[Double](nrows, ncols) + for (i <- intWrapper(0) until nrows) + for (j <- 0 until ncols) { + var x = 0.0 + for (k <- 0 until n) + x += xs(i)(k) * ys(k)(j) + zs(i)(j) = x + } + zs + } + + implicit class intArrayOps(arr: Array[Int]) { + transparent def foreach(op: => Int => Unit): Unit = { + var i = 0 + while (i < arr.length) { + op(arr(i)) + i += 1 + } + } + } + + def sum(ints: Array[Int]): Int = { + var t = 0 + for (n <- ints) t += n + t + } + + def main(args: Array[String]) = { + 1.until(10).foreach(i => println(i)) + 1.until(10).foreach(println(_)) + 1.until(10).foreach(println) + for (i <- 1 to 10) println(i) + + for (k1 <- 1 to 10) + for (k2 <- 1 to 10) + println(s"$k1") + + val xs = Array(1, 2, 3, 4) + assert(sum(xs) == 10, sum(xs)) + } +} diff --git a/tests/run/transparentPower.check b/tests/run/transparentPower.check new file mode 100644 index 000000000000..25e11563452f --- /dev/null +++ b/tests/run/transparentPower.check @@ -0,0 +1,2 @@ +1024.0 +2048.0 diff --git a/tests/run/transparentPower/Test_2.scala b/tests/run/transparentPower/Test_2.scala new file mode 100644 index 000000000000..8e16587b5653 --- /dev/null +++ b/tests/run/transparentPower/Test_2.scala @@ -0,0 +1,9 @@ +import p.pow.power +object Test { + + def main(args: Array[String]): Unit = { + println(power(2.0, 10)) + def x = 2.0 + println(power(x, 11)) + } +} diff --git a/tests/run/transparentPower/power_1.scala b/tests/run/transparentPower/power_1.scala new file mode 100644 index 000000000000..ef43488ce387 --- /dev/null +++ b/tests/run/transparentPower/power_1.scala @@ -0,0 +1,12 @@ +package p + +object pow { + + transparent def power(x: Double, n: Int): Double = + if (n == 0) 1.0 + else if (n == 1) x + else { + val y = power(x, n / 2) + if (n % 2 == 0) y * y else y * y * x + } +} diff --git a/tests/run/transparentPrivates.scala b/tests/run/transparentPrivates.scala new file mode 100644 index 000000000000..8498203af9ba --- /dev/null +++ b/tests/run/transparentPrivates.scala @@ -0,0 +1,36 @@ +object Test { + + class C[T](private val x: T) { + + private def foo[Z](z: Z): T = x + + private var y: T = _ + + transparent def get1 = x + transparent def get2[U](c: C[U]) = c.x + + transparent def foo1(x: Int) = foo(x) + transparent def foo2[U](c: C[U]) = c.foo(x) + + transparent def set1(z: T) = { y = z; y } + transparent def set2[U](c: C[U]) = { c.y = c.x; c.y } + } + + object CC { + private val x = 3 + transparent def get1 = x + } + + def main(args: Array[String]) = { + val cc = new C(2) + assert(cc.get1 == 2) + assert(cc.get2(cc) == 2) + assert(cc.foo1(1) == 2) + assert(cc.foo2(cc) == 2) + assert(cc.set1(3) == 3) + assert(cc.set2(cc) == 2) + + assert(CC.get1 == 3) + } + +} diff --git a/tests/run/transparentProtected.scala b/tests/run/transparentProtected.scala new file mode 100644 index 000000000000..478f599b2293 --- /dev/null +++ b/tests/run/transparentProtected.scala @@ -0,0 +1,22 @@ +package P { + class C { + protected def f(): Int = 22 + } +} + +package Q { + class D extends P.C { + class Inner { + transparent def g() = f() + } + } +} + +object Test extends App { + import Q._ + + val d = new D + val i = new d.Inner + val x = i.g() + assert(x == 22) +} diff --git a/tests/run/typelevel.scala b/tests/run/typelevel.scala index 9a836565ef51..00b06895cc74 100644 --- a/tests/run/typelevel.scala +++ b/tests/run/typelevel.scala @@ -1,31 +1,161 @@ -object Test extends App { - trait Nat +trait Nat { + def toInt: Int +} - case object Z extends Nat - type Z = Z.type - case class S[N <: Nat](n: Nat) extends Nat +case object Z extends Nat { + transparent def toInt = 0 +} + +case class S[N <: Nat](n: N) extends Nat { + transparent def toInt = n.toInt + 1 +} + +trait HList { + def length: Int + def head: Any + def tail: HList + transparent def isEmpty: Boolean = + length == 0 +} - abstract class HasResult[T] { type Result = T } - case class ToNat[+T](val value: T) extends HasResult[T] +// () +case object HNil extends HList { + transparent def length = 0 + def head: Nothing = ??? + def tail: Nothing = ??? +} + +// (H, T) +@annotation.showAsInfix(true) +case class HCons [H, T <: HList](hd: H, tl: T) extends HList { + transparent def length = 1 + tl.length + def head: H = this.hd + def tail: T = this.tl +} + +case class ToNat[T](val value: T) { + type Result = T +} + +object Test extends App { + type HNil = HNil.type + type Z = Z.type transparent def ToNat(inline n: Int): ToNat[Nat] = if n == 0 then new ToNat(Z) else { val n1 = ToNat(n - 1) - new ToNat[S[n1.Result]](S(n1.value)) + new ToNat(S(n1.value)) } val x0 = ToNat(0) - //val y0: Z = x0.value - //val z0: x0.Result = y0 + val y0: Z = x0.value + val z0: x0.Result = y0 val x1 = ToNat(1) - //val y1: S[Z] = x1.value - //val z1: x1.Result = y1 + val y1: S[Z] = x1.value + val z1: x1.Result = y1 val x2 = ToNat(2) - //val y2: S[S[Z]] = x2.value - //val z2: x2.Result = y2 + val y2: S[S[Z]] = x2.value + val z2: x2.Result = y2 println(x0) println(x1) println(x2) + transparent val i0 = y0.toInt + val j0: 0 = i0 + transparent val i2 = y2.toInt + val j2: 2 = i2 + + class HListDeco(private val as: HList) extends AnyVal { + transparent def :: [H] (a: H) = HCons(a, as) + transparent def ++ (bs: HList) = concat(as, bs) + } + transparent def concat(xs: HList, ys: HList): HList = + if xs.isEmpty then ys + else HCons(xs.head, concat(xs.tail, ys)) + + val xs = HCons(1, HCons("a", HNil)) + + val r0 = concat(HNil, HNil) + val r1 = concat(HNil, xs) + val r2 = concat(xs, HNil) + val r3 = concat(xs, xs) + + val r4 = concat(HNil, HCons(1, HCons("a", HNil))) + val r5 = concat(HCons(1, HCons("a", HNil)) , HNil) + val r6 = concat(HCons(1, HCons("a", HNil)), HCons(true, HCons(1.0, HNil))) + + transparent def size(xs: HList): Nat = + if xs.isEmpty then Z + else S(size(xs.tail)) + + transparent def sizeDefensive(xs: HList): Nat = xs.isEmpty match { + case true => Z + case false => S(sizeDefensive(xs.tail)) + } + + val s0 = size(HNil) + val s1 = size(xs) + transparent val l0 = HNil.length + val l0a: 0 = l0 + transparent val l1 = xs.length + val l1a: 2 = l1 + + transparent def index(xs: HList, inline idx: Int): Any = + if idx == 0 then xs.head + else index(xs.tail, idx - 1) + + val s2 = index(xs, 0) + val ss2: Int = s2 + val s3 = index(xs, 1) + var ss3: String = s3 + def s4 = index(xs, 2) + def ss4: Nothing = s4 + val s5 = index(xs, xs.length - 1) + val ss5: String = s5 + + + //val ys = 1 :: "a" :: HNil + + transparent implicit def hlistDeco(xs: HList): HListDeco = new HListDeco(xs) + + val rr0 = new HListDeco(HNil).++(HNil) + val rr1 = HNil ++ xs + val rr2 = xs ++ HNil + val rr3 = xs ++ xs + val rr3a: HCons[Int, HCons[String, HCons[Int, HCons[String, HNil]]]] = rr3 + + transparent def f(c: Boolean): Nat = { + def g[X <: Nat](x: X): X = x + g(if (c) Z else S(Z)) + } + + val f1: Z = f(true) + val f2: S[Z] = f(false) + + transparent def mapHead[T, R](t: T)(implicit fh: T => R): R = fh(t) + transparent def map(xs: HList): HList = { + + if (xs.isEmpty) HNil + else HCons(mapHead(xs.head), map(xs.tail)) + } + + implicit def mapInt: Int => Boolean = (i: Int) => i < 23 + implicit val mapString: String => Int = (s: String) => s.length + implicit val mapBoolean: Boolean => String = (b: Boolean) => if(b) "yes" else "no" + + val res = map(HCons(23, HCons("foo", HCons(true, HNil)))) + val res1: Boolean `HCons` (Int `HCons` (String `HCons` HNil)) = res + +/* + transparent def toInt1[T]: Int = type T match { + case Z => 0 + case S[type N] => toInt[N] + 1 + } + + transparent def toInt1[T]: Nat = implicit match { + case C[type T, type U], T =:= U => + case T <:< S[type N] => toInt[N] + 1 + } +*/ } \ No newline at end of file diff --git a/tests/run/typelevel1.scala b/tests/run/typelevel1.scala new file mode 100644 index 000000000000..18241a1990db --- /dev/null +++ b/tests/run/typelevel1.scala @@ -0,0 +1,53 @@ + +trait HList { + def length: Int + def head: Any + def tail: HList + + transparent def isEmpty: Boolean = length == 0 +} + +case object HNil extends HList { + transparent def length = 0 + def head: Nothing = ??? + def tail: Nothing = ??? +} + +case class :: [H, T <: HList] (hd: H, tl: T) extends HList { + transparent def length = 1 + tl.length + def head: H = this.hd + def tail: T = this.tl +} + +object Test extends App { + type HNil = HNil.type + + class HListDeco(val as: HList) extends AnyVal { + transparent def :: [H] (a: H): HList = new :: (a, as) + transparent def ++ (bs: HList): HList = concat(as, bs) + transparent def apply(idx: Int): Any = index(as, idx) + } + + transparent implicit def hlistDeco(xs: HList): HListDeco = new HListDeco(xs) + + transparent def concat[T1, T2](xs: HList, ys: HList): HList = + if xs.isEmpty then ys + else new ::(xs.head, concat(xs.tail, ys)) + + val xs = 1 :: "a" :: "b" :: HNil + val ys = true :: 1.0 :: HNil + val zs = concat(xs, ys) + + val control: Int :: String :: String :: Boolean :: Double :: HNil = zs + + transparent def index(xs: HList, idx: Int): Any = + if idx == 0 then xs.head + else index(xs.tail, idx - 1) + + val zs0 = index(zs, 0) + val zs1 = zs(1) + val zs2 = zs(2) + val zs3 = zs(3) + val zs4 = zs(4) + def zs5 = zs(5) +} \ No newline at end of file diff --git a/tests/run/typelevel2.scala b/tests/run/typelevel2.scala new file mode 100644 index 000000000000..f7768b7ca11f --- /dev/null +++ b/tests/run/typelevel2.scala @@ -0,0 +1,48 @@ +trait Nat { + def toInt: Int +} + +case object Z extends Nat { + transparent def toInt = 0 +} + +case class S[N <: Nat](n: N) extends Nat { + transparent def toInt = n.toInt + 1 +} + +object Test extends App { + type Z = Z.type + + type IntOrString(x: Boolean) = if x then Int else String + + val x: IntOrString(true) = 1 + val x1: Int = x + val y: IntOrString(false) = "" + val y1: String = y + + type ToNat(n: Int) <: Nat = + if n == 0 then Z + else S[ToNat(n - 1)] + + val n0: ToNat(0) = Z + val n1: ToNat(1) = S(Z) + val n3: ToNat(3) = S(S(S(Z))) + val i0: 0 = n0.toInt + val i1: 1 = n1.toInt + val i3: 3 = n3.toInt + + def ii: Int = 2 + def nn: ToNat(ii) = S(S(Z)) // no expansion possible, since `ii` is of type `Int`. + val ii1: Int = nn.toInt + + type nth[F[_], T](n: Int) = + if n == 0 then T + else F[nth[F, T](n - 1)] + + val nth0: nth[List, Int](0) = 2 + val nth1: nth[Option, String](1) = Some("hi") + val nth3: nth[Seq, Boolean](3) = Seq(Seq(Seq(true, false))) + val nth0a: Int = nth0 + val nth1a: Option[String] = nth1 + val nth3a: Seq[Seq[Seq[Boolean]]] = nth3 +} \ No newline at end of file