diff options
Diffstat (limited to 'compiler')
56 files changed, 965 insertions, 434 deletions
diff --git a/compiler/sjs/backend/sjs/JSCodeGen.scala b/compiler/sjs/backend/sjs/JSCodeGen.scala index 401e01784..69a5651fc 100644 --- a/compiler/sjs/backend/sjs/JSCodeGen.scala +++ b/compiler/sjs/backend/sjs/JSCodeGen.scala @@ -127,7 +127,7 @@ class JSCodeGen()(implicit ctx: Context) { /* Finally, we emit true code for the remaining class defs. */ for (td <- allTypeDefs) { val sym = td.symbol - implicit val pos: Position = sym.pos + implicit val pos = sym.pos /* Do not actually emit code for primitive types nor scala.Array. */ val isPrimitive = @@ -203,7 +203,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genScalaClass(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos: Position = sym.pos + implicit val pos = sym.pos assert(!sym.is(Trait), "genScalaClass() must be called only for normal classes: "+sym) @@ -336,7 +336,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genRawJSClassData(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos: Position = sym.pos + implicit val pos = sym.pos val classIdent = encodeClassFullNameIdent(sym) val superClass = @@ -358,7 +358,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genInterface(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos: Position = sym.pos + implicit val pos = sym.pos val classIdent = encodeClassFullNameIdent(sym) @@ -408,7 +408,7 @@ class JSCodeGen()(implicit ctx: Context) { f <- classSym.info.decls if !f.is(Method) && f.isTerm } yield { - implicit val pos: Position = f.pos + implicit val pos = f.pos val name = /*if (isExposed(f)) js.StringLiteral(jsNameOf(f)) @@ -479,7 +479,7 @@ class JSCodeGen()(implicit ctx: Context) { * Other (normal) methods are emitted with `genMethodBody()`. */ private def genMethodWithCurrentLocalNameScope(dd: DefDef): Option[js.MethodDef] = { - implicit val pos: Position = dd.pos + implicit val pos = dd.pos val sym = dd.symbol val vparamss = dd.vparamss val rhs = dd.rhs @@ -501,7 +501,7 @@ class JSCodeGen()(implicit ctx: Context) { val methodName: js.PropertyName = encodeMethodSym(sym) def jsParams = for (param <- params) yield { - implicit val pos: Position = param.pos + implicit val pos = param.pos js.ParamDef(encodeLocalSym(param), toIRType(param.info), mutable = false, rest = false) } @@ -574,13 +574,13 @@ class JSCodeGen()(implicit ctx: Context) { private def genMethodDef(static: Boolean, methodName: js.PropertyName, paramsSyms: List[Symbol], resultIRType: jstpe.Type, tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos ctx.debuglog("genMethod " + methodName.name) ctx.debuglog("") val jsParams = for (param <- paramsSyms) yield { - implicit val pos: Position = param.pos + implicit val pos = param.pos js.ParamDef(encodeLocalSym(param), toIRType(param.info), mutable = false, rest = false) } @@ -621,7 +621,7 @@ class JSCodeGen()(implicit ctx: Context) { /* Any JavaScript expression is also a statement, but at least we get rid * of some pure expressions that come from our own codegen. */ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos tree match { case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr)) case _:js.Literal | js.This() => js.Skip() @@ -644,7 +644,7 @@ class JSCodeGen()(implicit ctx: Context) { * is transformed into an equivalent portion of the JS AST. */ private def genStatOrExpr(tree: Tree, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos ctx.debuglog(" " + tree) ctx.debuglog("") @@ -902,7 +902,7 @@ class JSCodeGen()(implicit ctx: Context) { * primitives, JS calls, etc. They are further dispatched in here. */ private def genApply(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val args = tree.args val sym = tree.fun.symbol @@ -951,7 +951,7 @@ class JSCodeGen()(implicit ctx: Context) { * irrelevant. */ private def genSuperCall(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val Apply(fun @ Select(sup @ Super(_, mix), _), args) = tree val sym = fun.symbol @@ -987,7 +987,7 @@ class JSCodeGen()(implicit ctx: Context) { * * regular new */ private def genApplyNew(tree: Apply): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree val ctor = fun.symbol @@ -1023,7 +1023,7 @@ class JSCodeGen()(implicit ctx: Context) { private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = { import scala.tools.nsc.backend.ScalaPrimitives._ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val Apply(fun, args) = tree val receiver = qualifierOf(fun) @@ -1063,7 +1063,7 @@ class JSCodeGen()(implicit ctx: Context) { private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = { import scala.tools.nsc.backend.ScalaPrimitives._ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val genArg = genExpr(arg) val resultIRType = toIRType(tree.tpe) @@ -1118,7 +1118,7 @@ class JSCodeGen()(implicit ctx: Context) { } import OpTypes._ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val lhsIRType = toIRType(lhs.tpe) val rhsIRType = toIRType(rhs.tpe) @@ -1374,7 +1374,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genStringConcat(tree: Apply, receiver: Tree, args: List[Tree]): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val arg = args.head @@ -1401,7 +1401,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for a call to Any.## */ private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos genModuleApplyMethod(defn.ScalaRuntimeModule.requiredMethod(nme.hash_), List(genExpr(receiver))) @@ -1411,7 +1411,7 @@ class JSCodeGen()(implicit ctx: Context) { private def genArrayOp(tree: Tree, code: Int): js.Tree = { import scala.tools.nsc.backend.ScalaPrimitives._ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val Apply(fun, args) = tree val arrayObj = qualifierOf(fun) @@ -1462,7 +1462,7 @@ class JSCodeGen()(implicit ctx: Context) { // common case for which there is no side-effect nor NPE genArg case _ => - implicit val pos: Position = tree.pos + implicit val pos = tree.pos /* TODO Check for a null receiver? * In theory, it's UB, but that decision should be left for link time. */ @@ -1474,7 +1474,7 @@ class JSCodeGen()(implicit ctx: Context) { private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = { import scala.tools.nsc.backend.ScalaPrimitives._ - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val source = genExpr(receiver) @@ -1544,7 +1544,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen a call to the special `throw` method. */ private def genThrow(tree: Apply, args: List[Tree]): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val exception = args.head val genException = genExpr(exception) js.Throw { @@ -1568,7 +1568,7 @@ class JSCodeGen()(implicit ctx: Context) { * * Regular method call */ private def genNormalApply(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val fun = tree.fun match { case fun: Ident => desugarIdent(fun).get @@ -1616,7 +1616,7 @@ class JSCodeGen()(implicit ctx: Context) { superIn: Option[Symbol] = None)( implicit pos: Position): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos def noSpread = !args.exists(_.isInstanceOf[js.JSSpread]) val argc = args.size // meaningful only for methods that don't have varargs @@ -1775,7 +1775,7 @@ class JSCodeGen()(implicit ctx: Context) { * primitive instead.) */ private def genTypeApply(tree: TypeApply): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val TypeApply(fun, targs) = tree @@ -1803,7 +1803,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for a Java Seq literal. */ private def genJavaSeqLiteral(tree: JavaSeqLiteral): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val genElems = tree.elems.map(genExpr) val arrayType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType] @@ -1852,7 +1852,7 @@ class JSCodeGen()(implicit ctx: Context) { * available in the `body`. */ private def genClosure(tree: Closure): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val Closure(env, call, functionalInterface) = tree val envSize = env.size @@ -1868,7 +1868,7 @@ class JSCodeGen()(implicit ctx: Context) { val allCaptureValues = qualifier :: env val (formalCaptures, actualCaptures) = allCaptureValues.map { value => - implicit val pos: Position = value.pos + implicit val pos = value.pos val formalIdent = value match { case Ident(name) => freshLocalIdent(name.toString) case This(_) => freshLocalIdent("this") @@ -1988,7 +1988,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for an isInstanceOf test (for reference types only) */ private def genIsInstanceOf(tree: Tree, value: js.Tree, to: Type): js.Tree = { - implicit val pos: Position = tree.pos + implicit val pos = tree.pos val sym = to.widenDealias.typeSymbol if (sym == defn.ObjectClass) { @@ -2242,7 +2242,7 @@ class JSCodeGen()(implicit ctx: Context) { * to perform the conversion to js.Array, then wrap in a Spread * operator. */ - implicit val pos: Position = arg.pos + implicit val pos = arg.pos val jsArrayArg = genModuleApplyMethod( jsdefn.RuntimePackage_genTraversableOnce2jsArray, List(genExpr(arg))) @@ -2259,7 +2259,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def tryGenRepeatedParamAsJSArray(arg: Tree, handleNil: Boolean): Option[List[js.Tree]] = { - implicit val pos: Position = arg.pos + implicit val pos = arg.pos // Given a method `def foo(args: T*)` arg match { diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 397382c2f..c17a32744 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -39,6 +39,10 @@ import dotty.tools.dotc.core.Names.TypeName import scala.annotation.tailrec class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{ + import Symbols.{toDenot, toClassDenot} + // Dotty deviation: Need to (re-)import implicit decorators here because otherwise + // they would be shadowed by the more deeply nested `symHelper` decorator. + type Symbol = Symbols.Symbol type Type = Types.Type type Tree = tpd.Tree @@ -206,18 +210,15 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma implicit val ConstantClassTag: ClassTag[Constant] = ClassTag[Constant](classOf[Constant]) implicit val ClosureTag: ClassTag[Closure] = ClassTag[Closure](classOf[Closure]) - /* dont emit any annotations for now*/ - def isRuntimeVisible(annot: Annotation): Boolean = { - annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr) match { - case Some(retentionAnnot) => - retentionAnnot.tree.find(_.symbol == AnnotationRetentionRuntimeAttr).isDefined - case _ => - // SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the - // annotation is emitted with visibility `RUNTIME` - // dotty bug: #389 - true + def isRuntimeVisible(annot: Annotation): Boolean = + if (toDenot(annot.atp.typeSymbol).hasAnnotation(AnnotationRetentionAttr)) + retentionPolicyOf(annot) == AnnotationRetentionRuntimeAttr + else { + // SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the + // annotation is emitted with visibility `RUNTIME` + // dotty bug: #389 + true } - } def shouldEmitAnnotation(annot: Annotation): Boolean = { annot.symbol.isJavaDefined && @@ -227,7 +228,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma private def retentionPolicyOf(annot: Annotation): Symbol = annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr). - flatMap(_.argument(0).map(_.symbol)).getOrElse(AnnotationRetentionClassAttr) + flatMap(_.argumentConstant(0).map(_.symbolValue)).getOrElse(AnnotationRetentionClassAttr) private def emitArgument(av: AnnotationVisitor, name: String, @@ -686,8 +687,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev)) } //todo is handled specially for JavaDefined symbols in scalac - - // members def primaryConstructor: Symbol = toDenot(sym).primaryConstructor @@ -708,7 +707,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } else Nil - def annotations: List[Annotation] = Nil + def annotations: List[Annotation] = toDenot(sym).annotations def companionModuleMembers: List[Symbol] = { // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes, // not local classes of the companion module (E in the exmaple) that were lifted by lambdalift. @@ -1027,7 +1026,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma case JavaArrayType(elem) => elem case _ => ctx.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", field.pos) - ErrorType + UnspecifiedErrorType } def _2: List[Tree] = field.elems } diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index 0027defa7..89831e56b 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Names.TermName import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Types.{JavaArrayType, ErrorType, Type} +import dotty.tools.dotc.core.Types.{JavaArrayType, UnspecifiedErrorType, Type} import scala.collection.{ mutable, immutable } @@ -73,7 +73,7 @@ class DottyPrimitives(ctx: Context) { case JavaArrayType(el) => el case _ => ctx.error(s"expected Array $tpe") - ErrorType + UnspecifiedErrorType } code match { @@ -125,7 +125,7 @@ class DottyPrimitives(ctx: Context) { /** Initialize the primitive map */ private def init: immutable.Map[Symbol, Int] = { - implicit val ctx: Context = this.ctx + implicit val ctx = this.ctx import core.Symbols.defn val primitives = new mutable.HashMap[Symbol, Int]() diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ad3249be2..900d2b0e3 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -61,6 +61,7 @@ class Compiler { new PatternMatcher, // Compile pattern matches new ExplicitOuter, // Add accessors to outer classes from nested ones. new ExplicitSelf, // Make references to non-trivial self types explicit as casts + new ShortcutImplicits, // Allow implicit functions without creating closures new CrossCastAnd, // Normalize selections involving intersection types. new Splitter), // Expand selections involving union types into conditionals List(new VCInlineMethods, // Inlines calls to value class methods diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 15cb0b665..db78cfffb 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -24,7 +24,6 @@ object desugar { /** Names of methods that are added unconditionally to case classes */ def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean = - name == nme.isDefined || name == nme.copy || name == nme.productArity || name.isSelectorName @@ -124,6 +123,13 @@ object desugar { else vdef } + def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean)(implicit ctx: Context) = + for (tpt <- tpts) yield { + val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param + val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName + ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit) + } + /** Expand context bounds to evidence params. E.g., * * def f[T >: L <: H : B](params) @@ -144,19 +150,16 @@ object desugar { val epbuf = new ListBuffer[ValDef] def desugarContextBounds(rhs: Tree): Tree = rhs match { case ContextBounds(tbounds, cxbounds) => - for (cxbound <- cxbounds) { - val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param - val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName - epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit) - } + for (cxbound <- cxbounds) + epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor) tbounds case PolyTypeTree(tparams, body) => cpy.PolyTypeTree(rhs)(tparams, desugarContextBounds(body)) case _ => rhs } - val tparams1 = tparams mapConserve { tdef => - cpy.TypeDef(tdef)(rhs = desugarContextBounds(tdef.rhs)) + val tparams1 = tparams mapConserve { tparam => + cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs)) } val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList) @@ -343,10 +346,10 @@ object desugar { if (isCaseClass) { def syntheticProperty(name: TermName, rhs: Tree) = DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic) - val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) val caseParams = constrVparamss.head.toArray - val productElemMeths = for (i <- 0 until arity) yield - syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) + val productElemMeths = + for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) + yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) def isRepeated(tree: Tree): Boolean = tree match { case PostfixOp(_, nme.raw.STAR) => true case ByNameTypeTree(tree1) => isRepeated(tree1) @@ -369,7 +372,7 @@ object desugar { DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) .withMods(synthetic) :: Nil } - copyMeths ::: isDefinedMeth :: productElemMeths.toList + copyMeths ::: productElemMeths.toList } else Nil @@ -681,6 +684,11 @@ object desugar { Function(param :: Nil, Block(vdefs, body)) } + def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = { + val params = makeImplicitParameters(formals.map(TypeTree), forPrimaryConstructor = false) + new ImplicitFunction(params, body) + } + /** Add annotation with class `cls` to tree: * tree @cls */ diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index d1e6bd38a..da83d0644 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -290,6 +290,16 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case _ => false } + /** Is `tree` an implicit function or closure, possibly nested in a block? */ + def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match { + case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit) + case Closure(_, meth, _) => true + case Block(Nil, expr) => isImplicitClosure(expr) + case Block(DefDef(nme.ANON_FUN, _, (param :: _) :: _, _, _) :: Nil, _: Closure) => + param.mods.is(Implicit) + case _ => false + } + // todo: fill with other methods from TreeInfo that only apply to untpd.Tree's } @@ -501,7 +511,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => */ object closure { def unapply(tree: Tree): Option[(List[Tree], Tree, Tree)] = tree match { - case Block(_, Closure(env, meth, tpt)) => Some(env, meth, tpt) + case Block(_, expr) => unapply(expr) case Closure(env, meth, tpt) => Some(env, meth, tpt) case _ => None } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c4b2b2122..798f0f567 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -113,7 +113,7 @@ object Trees { * type. (Overridden by empty trees) */ def withType(tpe: Type)(implicit ctx: Context): ThisTree[Type] = { - if (tpe == ErrorType) assert(ctx.reporter.errorsReported) + if (tpe.isInstanceOf[ErrorType]) assert(ctx.reporter.errorsReported) withTypeUnchecked(tpe) } @@ -890,6 +890,11 @@ object Trees { case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree case _ => finalize(tree, untpd.Select(qualifier, name)) } + /** Copy Ident or Select trees */ + def Ref(tree: RefTree)(name: Name)(implicit ctx: Context) = tree match { + case Ident(_) => Ident(tree)(name) + case Select(qual, _) => Select(tree)(qual, name) + } def This(tree: Tree)(qual: untpd.Ident): This = tree match { case tree: This if qual eq tree.qual => tree case _ => finalize(tree, untpd.This(qual)) @@ -1224,7 +1229,7 @@ object Trees { case AppliedTypeTree(tpt, args) => this(this(x, tpt), args) case PolyTypeTree(tparams, body) => - implicit val ctx: Context = localCtx + implicit val ctx = localCtx this(this(x, tparams), body) case ByNameTypeTree(result) => this(x, result) @@ -1237,13 +1242,13 @@ object Trees { case UnApply(fun, implicits, patterns) => this(this(this(x, fun), implicits), patterns) case tree @ ValDef(name, tpt, _) => - implicit val ctx: Context = localCtx + implicit val ctx = localCtx this(this(x, tpt), tree.rhs) case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx: Context = localCtx + implicit val ctx = localCtx this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) case TypeDef(name, rhs) => - implicit val ctx: Context = localCtx + implicit val ctx = localCtx this(x, rhs) case tree @ Template(constr, parents, self, _) => this(this(this(this(x, constr), parents), self), tree.body) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index cd6b3fcf2..433808e8e 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -450,7 +450,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } else foldOver(sym, tree) } - override val cpy = new TypedTreeCopier + override val cpy: TypedTreeCopier = // Type ascription needed to pick up any new members in TreeCopier (currently there are none) + new TypedTreeCopier class TypedTreeCopier extends TreeCopier { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] = diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 6c5210287..f3ffce8f8 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -53,6 +53,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isTerm = body.isTerm override def isType = body.isType } + + /** An implicit function type */ + class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) { + override def toString = s"ImplicitFunction($args, $body)" + } + /** A function created from a wildcard expression * @param placeHolderParams a list of definitions of synthetic parameters * @param body the function body where wildcards are replaced by @@ -111,7 +117,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Var() extends Mod(Flags.Mutable) - case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag) + case class Implicit() extends Mod(Flags.ImplicitCommon) case class Final() extends Mod(Flags.Final) @@ -270,8 +276,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { // ------ Additional creation methods for untyped only ----------------- - // def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd - /** new pre.C[Ts](args1)...(args_n) * ==> * (new pre.C).<init>[Ts](args1)...(args_n) diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index a695202d3..b5bfbb39f 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -18,6 +18,7 @@ class JavaPlatform extends Platform { currentClassPath = Some(new PathResolver().result) val cp = currentClassPath.get //println(cp) + //println("------------------") cp } diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala index aa4d8aeb0..8df9a8c0e 100644 --- a/compiler/src/dotty/tools/dotc/config/PathResolver.scala +++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala @@ -143,7 +143,7 @@ object PathResolver { println(Defaults) } else { - implicit val ctx: Context = (new ContextBase).initialCtx // Dotty deviation: implicits need explicit type + implicit val ctx = (new ContextBase).initialCtx val ArgsSummary(sstate, rest, errors) = ctx.settings.processArguments(args.toList, true) errors.foreach(println) diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index 1e623db4d..2559209c3 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -119,7 +119,7 @@ object Comments { def apply(comment: Comment, code: String, codePos: Position)(implicit ctx: Context) = new UseCase(comment, code, codePos) { val untpdCode = { - val tree = new Parser(new SourceFile("<usecase>", code)).localDef(codePos.start, EmptyFlags) + val tree = new Parser(new SourceFile("<usecase>", code)).localDef(codePos.start) tree match { case tree: untpd.DefDef => diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index a1b99d16d..29629e505 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -262,6 +262,9 @@ object Contexts { final def withPhaseNoLater(phase: Phase) = if (phase.exists && ctx.phase.id > phase.id) withPhase(phase) else ctx + final def withPhaseNoEarlier(phase: Phase) = + if (phase.exists && ctx.phase.id < phase.id) withPhase(phase) else ctx + /** If -Ydebug is on, the top of the stack trace where this context * was created, otherwise `null`. */ diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index e4e5761b2..45e37eb8b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -86,24 +86,51 @@ class Definitions { newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered } - /** The trait FunctionN, for some N */ - private def newFunctionNTrait(n: Int) = { + /** The trait FunctionN or ImplicitFunctionN, for some N + * @param name The name of the trait to be created + * + * FunctionN traits follow this template: + * + * trait FunctionN[T0,...T{N-1}, R] extends Object { + * def apply($x0: T0, ..., $x{N_1}: T{N-1}): R + * } + * + * That is, they follow the template given for Function2..Function22 in the + * standard library, but without `tupled` and `curried` methods and without + * a `toString`. + * + * ImplicitFunctionN traits follow this template: + * + * trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object with FunctionN[T0,...,T{N-1}, R] { + * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R + * } + */ + private def newFunctionNTrait(name: TypeName) = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope + val arity = name.functionArity val argParams = - for (i <- List.range(0, n)) yield - enterTypeParam(cls, s"T$i".toTypeName, Contravariant, decls) - val resParam = enterTypeParam(cls, s"R".toTypeName, Covariant, decls) + for (i <- List.range(0, arity)) yield + enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls) + val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls) + val (methodType, parentTraits) = + if (name.startsWith(tpnme.ImplicitFunction)) { + val superTrait = + FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) + (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) + } + else (MethodType, Nil) val applyMeth = decls.enter( newMethod(cls, nme.apply, - MethodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) - denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) + methodType(argParams.map(_.typeRef), resParam.typeRef), Deferred)) + denot.info = + ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls) } } - newClassSymbol(ScalaPackageClass, s"Function$n".toTypeName, Trait, completer) + newClassSymbol(ScalaPackageClass, name, Trait, completer) } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = @@ -590,15 +617,16 @@ class Definitions { sym.owner.linkedClass.typeRef object FunctionOf { - def apply(args: List[Type], resultType: Type)(implicit ctx: Context) = - FunctionType(args.length).appliedTo(args ::: resultType :: Nil) + def apply(args: List[Type], resultType: Type, isImplicit: Boolean = false)(implicit ctx: Context) = + FunctionType(args.length, isImplicit).appliedTo(args ::: resultType :: Nil) def unapply(ft: Type)(implicit ctx: Context) = { val tsym = ft.typeSymbol - if (isFunctionClass(tsym)) { - lazy val targs = ft.argInfos + val isImplicitFun = isImplicitFunctionClass(tsym) + if (isImplicitFun || isFunctionClass(tsym)) { + val targs = ft.argInfos val numArgs = targs.length - 1 - if (numArgs >= 0 && FunctionType(numArgs).symbol == tsym) - Some(targs.init, targs.last) + if (numArgs >= 0 && FunctionType(numArgs, isImplicitFun).symbol == tsym) + Some(targs.init, targs.last, isImplicitFun) else None } else None @@ -659,8 +687,12 @@ class Definitions { lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply) def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol - def FunctionType(n: Int)(implicit ctx: Context): TypeRef = - if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n) + def ImplicitFunctionClass(n: Int)(implicit ctx: Context) = + ctx.requiredClass("scala.ImplicitFunction" + n.toString) + + def FunctionType(n: Int, isImplicit: Boolean = false)(implicit ctx: Context): TypeRef = + if (isImplicit && !ctx.erasedTypes) ImplicitFunctionClass(n).typeRef + else if (n < MaxImplementedFunctionArity) ImplementedFunctionType(n) else FunctionClass(n).typeRef private lazy val TupleTypes: Set[TypeRef] = TupleType.toSet @@ -675,7 +707,9 @@ class Definitions { private def isVarArityClass(cls: Symbol, prefix: Name) = { val name = scalaClassName(cls) - name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) + name.startsWith(prefix) && + name.length > prefix.length && + name.drop(prefix.length).forall(_.isDigit) } def isBottomClass(cls: Symbol) = @@ -684,6 +718,7 @@ class Definitions { tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) + def isImplicitFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.ImplicitFunction) def isUnimplementedFunctionClass(cls: Symbol) = isFunctionClass(cls) && cls.name.functionArity > MaxImplementedFunctionArity def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) @@ -735,14 +770,29 @@ class Definitions { def isProductSubType(tp: Type)(implicit ctx: Context) = (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) - def isFunctionType(tp: Type)(implicit ctx: Context) = - isFunctionClass(tp.dealias.typeSymbol) && { - val arity = functionArity(tp) - arity >= 0 && tp.isRef(FunctionType(functionArity(tp)).typeSymbol) - } + def productArity(tp: Type)(implicit ctx: Context) = + if (tp derivesFrom ProductType.symbol) + tp.baseClasses.find(isProductClass) match { + case Some(prod) => prod.typeParams.length + case None => -1 + } + else -1 + + /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN ? */ + def isFunctionType(tp: Type)(implicit ctx: Context) = { + val arity = functionArity(tp) + val sym = tp.dealias.typeSymbol + arity >= 0 && ( + isFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = false).typeSymbol) || + isImplicitFunctionClass(sym) && tp.isRef(FunctionType(arity, isImplicit = true).typeSymbol) + ) + } def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1 + def isImplicitFunctionType(tp: Type)(implicit ctx: Context) = + isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction) + // ----- primitive value class machinery ------------------------------------------ /** This class would also be obviated by the implicit function type design */ @@ -815,6 +865,9 @@ class Definitions { // ----- Initialization --------------------------------------------------- + private def maxImplemented(name: Name) = + if (name `startsWith` tpnme.Function) MaxImplementedFunctionArity else 0 + /** Give the scala package a scope where a FunctionN trait is automatically * added when someone looks for it. */ @@ -824,8 +877,8 @@ class Definitions { val newDecls = new MutableScope(oldDecls) { override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { val res = super.lookupEntry(name) - if (res == null && name.functionArity > 0) - newScopeEntry(newFunctionNTrait(name.functionArity)) + if (res == null && name.isTypeName && name.functionArity > maxImplemented(name)) + newScopeEntry(newFunctionNTrait(name.asTypeName)) else res } } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 7a4fc0512..c037d1ce7 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -188,6 +188,8 @@ object NameOps { def errorName: N = likeTyped(name ++ nme.ERROR) + def directName: N = likeTyped(name ++ DIRECT_SUFFIX) + def freshened(implicit ctx: Context): N = likeTyped( if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName @@ -229,11 +231,14 @@ object NameOps { } } - def functionArity: Int = - if (name.startsWith(tpnme.Function)) - try name.drop(tpnme.Function.length).toString.toInt - catch { case ex: NumberFormatException => -1 } - else -1 + def functionArity: Int = { + def test(prefix: Name): Int = + if (name.startsWith(prefix)) + try name.drop(prefix.length).toString.toInt + catch { case ex: NumberFormatException => -1 } + else -1 + test(tpnme.Function) max test(tpnme.ImplicitFunction) + } /** The name of the generic runtime operation corresponding to an array operation */ def genericArrayOp: TermName = name match { diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 741ff8b1f..716959648 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -129,6 +129,7 @@ object StdNames { val COMPANION_MODULE_METHOD: N = "companion$module" val COMPANION_CLASS_METHOD: N = "companion$class" val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" + val DIRECT_SUFFIX: N = "$direct" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. @@ -181,6 +182,7 @@ object StdNames { final val AnyVal: N = "AnyVal" final val ExprApi: N = "ExprApi" final val Function: N = "Function" + final val ImplicitFunction: N = "ImplicitFunction" final val Mirror: N = "Mirror" final val Nothing: N = "Nothing" final val Null: N = "Null" @@ -424,7 +426,6 @@ object StdNames { val info: N = "info" val inlinedEquals: N = "inlinedEquals" val isArray: N = "isArray" - val isDefined: N = "isDefined" val isDefinedAt: N = "isDefinedAt" val isDefinedAtImpl: N = "$isDefinedAt" val isEmpty: N = "isEmpty" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 8b7c28e19..aaae78c57 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -75,7 +75,7 @@ trait SymDenotations { this: Context => def explainSym(msg: String) = explain(s"$msg\n defined = ${denot.definedPeriodsString}") if (denot.is(ValidForever) || denot.isRefinementClass) true else { - implicit val ctx: Context = this + implicit val ctx = this val initial = denot.initial if ((initial ne denot) || ctx.phaseId != initial.validFor.firstPhaseId) { ctx.withPhase(initial.validFor.firstPhaseId).traceInvalid(initial) @@ -1937,12 +1937,12 @@ object SymDenotations { /** A completer for missing references */ class StubInfo() extends LazyType { - def initializeToDefaults(denot: SymDenotation)(implicit ctx: Context) = { + def initializeToDefaults(denot: SymDenotation, errMsg: => String)(implicit ctx: Context) = { denot.info = denot match { case denot: ClassDenotation => ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope) case _ => - ErrorType + new ErrorType(errMsg) } denot.privateWithin = NoSymbol } @@ -1954,13 +1954,14 @@ object SymDenotations { if (file != null) (s" in $file", file.toString) else ("", "the signature") val name = ctx.fresh.setSetting(ctx.settings.debugNames, true).nameString(denot.name) - ctx.error( + def errMsg = i"""bad symbolic reference. A signature$location |refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available. |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $src.""") + |the classpath might be incompatible with the version used when compiling $src.""" + ctx.error(errMsg) if (ctx.debug) throw new Error() - initializeToDefaults(denot) + initializeToDefaults(denot, errMsg) } } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index cfd85c49c..d355686ab 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -22,6 +22,7 @@ import NameOps._ import ast.tpd.Tree import ast.TreeTypeMap import Constants.Constant +import reporting.diagnostic.Message import Denotations.{ Denotation, SingleDenotation, MultiDenotation } import collection.mutable import io.AbstractFile @@ -290,9 +291,11 @@ trait Symbols { this: Context => */ def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp) - def newErrorSymbol(owner: Symbol, name: Name) = + def newErrorSymbol(owner: Symbol, name: Name, msg: => Message) = { + val errType = new ErrorType(msg) newSymbol(owner, name, SyntheticArtifact, - if (name.isTypeName) TypeAlias(ErrorType) else ErrorType) + if (name.isTypeName) TypeAlias(errType) else errType) + } /** Map given symbols, subjecting their attributes to the mappings * defined in the given TreeTypeMap `ttmap`. @@ -390,6 +393,10 @@ object Symbols { denot } + /** The initial denotation of this symbol, without going through `current` */ + final def initialDenot(implicit ctx: Context): SymDenotation = + lastDenot.initial + private[core] def defRunId: RunId = if (lastDenot == null) NoRunId else lastDenot.validFor.runId diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index f8c0cdac9..334306f19 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -16,7 +16,7 @@ import scala.util.control.NonFatal /** Provides methods to compare types. */ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { - implicit val ctx: Context = initctx + implicit val ctx = initctx val state = ctx.typerState import state.constraint @@ -156,7 +156,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def firstTry(tp1: Type, tp2: Type): Boolean = tp2 match { case tp2: NamedType => def compareNamed(tp1: Type, tp2: NamedType): Boolean = { - implicit val ctx: Context = this.ctx + implicit val ctx = this.ctx tp2.info match { case info2: TypeAlias => isSubType(tp1, info2.alias) case _ => tp1 match { @@ -265,7 +265,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case ConstantType(v1) => v1.value == v2.value case _ => secondTry(tp1, tp2) } - case ErrorType => + case _: FlexType => true case _ => secondTry(tp1, tp2) @@ -341,7 +341,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2) - case ErrorType => + case _: FlexType => true case _ => thirdTry(tp1, tp2) @@ -478,7 +478,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1 @ MethodType(_, formals1) => (tp1.signature consistentParams tp2.signature) && matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit && // needed? + (!tp1.isImplicit || tp2.isImplicit) && // non-implicit functions shadow implicit ones isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => false @@ -1003,9 +1003,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: MethodType => tp2.widen match { case tp2: MethodType => - tp1.isImplicit == tp2.isImplicit && - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) + // implicitness is ignored when matching + matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case tp2 => relaxed && tp1.paramNames.isEmpty && matchesType(tp1.resultType, tp2, relaxed) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 57397a8bc..91e37d440 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -2,7 +2,9 @@ package dotty.tools package dotc package core -import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined +import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._ +import Flags.JavaDefined +import NameOps._ import Uniques.unique import dotc.transform.ExplicitOuter._ import dotc.transform.ValueClasses._ @@ -42,7 +44,7 @@ object TypeErasure { val sym = tp.symbol sym.isClass && sym != defn.AnyClass && sym != defn.ArrayClass && - !defn.isUnimplementedFunctionClass(sym) + !defn.isUnimplementedFunctionClass(sym) && !defn.isImplicitFunctionClass(sym) case _: TermRef => true case JavaArrayType(elem) => @@ -55,7 +57,7 @@ object TypeErasure { tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) case tp @ ClassInfo(pre, _, parents, decls, _) => isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) - case NoType | NoPrefix | WildcardType | ErrorType | SuperType(_, _) => + case NoType | NoPrefix | WildcardType | _: ErrorType | SuperType(_, _) => true case _ => false @@ -327,6 +329,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean * - For a typeref scala.Any, scala.AnyVal or scala.Singleton: |java.lang.Object| * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. * - For a typeref scala.FunctionN, where N > MaxImplementedFunctionArity, scala.FunctionXXL + * - For a typeref scala.ImplicitFunctionN, | scala.FunctionN | * - For a typeref P.C where C refers to a class, <noprefix> # C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. @@ -356,6 +359,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp) else if (sym == defn.ArrayClass) apply(tp.appliedTo(TypeBounds.empty)) // i966 shows that we can hit a raw Array type. else if (defn.isUnimplementedFunctionClass(sym)) defn.FunctionXXLType + else if (defn.isImplicitFunctionClass(sym)) apply(defn.FunctionType(sym.name.functionArity)) else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent @@ -398,7 +402,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } - case NoType | NoPrefix | ErrorType | JavaArrayType(_) => + case NoType | NoPrefix | _: ErrorType | JavaArrayType(_) => tp case tp: WildcardType if wildcardOK => tp @@ -438,7 +442,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = { val cls = tref.symbol.asClass val underlying = underlyingOfValueClass(cls) - if (underlying.exists) ErasedValueType(tref, valueErasure(underlying)) + if (underlying.exists && !isCyclic(cls)) ErasedValueType(tref, valueErasure(underlying)) else NoType } @@ -492,7 +496,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean val erasedVCRef = eraseDerivedValueClassRef(tp) if (erasedVCRef.exists) return sigName(erasedVCRef) } - normalizeClass(sym.asClass).fullName.asTypeName + if (defn.isImplicitFunctionClass(sym)) + sigName(defn.FunctionType(sym.name.functionArity)) + else + normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayOf(elem) => sigName(this(tp)) case JavaArrayType(elem) => @@ -506,7 +513,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (inst.exists) sigName(inst) else tpnme.Uninstantiated case tp: TypeProxy => sigName(tp.underlying) - case ErrorType | WildcardType => + case _: ErrorType | WildcardType => tpnme.WILDCARD case tp: WildcardType => sigName(tp.optBounds) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 39214dd0c..f134412a7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -433,7 +433,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. formals = formals.updated(name, tp1.typeParamNamed(name)) } normalizeToRef(tp1) - case ErrorType => + case _: ErrorType => defn.AnyType case AnnotatedType(tpe, _) => normalizeToRef(tpe) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7e6620f8e..93c381c4c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -15,9 +15,10 @@ import SymDenotations._ import Decorators._ import Denotations._ import Periods._ -import util.Positions.Position +import util.Positions.{Position, NoPosition} import util.Stats._ import util.{DotClass, SimpleMap} +import reporting.diagnostic.Message import ast.tpd._ import ast.TreeTypeMap import printing.Texts._ @@ -176,7 +177,7 @@ object Types { /** Is this type produced as a repair for an error? */ final def isError(implicit ctx: Context): Boolean = stripTypeVar match { - case ErrorType => true + case _: ErrorType => true case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous) } @@ -387,8 +388,8 @@ object Types { tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix) case tp: TypeProxy => tp.underlying.findDecl(name, excluded) - case ErrorType => - ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name) + case err: ErrorType => + ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name, err.msg) case _ => NoDenotation } @@ -453,8 +454,8 @@ object Types { go(tp.join) case tp: JavaArrayType => defn.ObjectType.findMember(name, pre, excluded) - case ErrorType => - ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) + case err: ErrorType => + ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name, err.msg) case _ => NoDenotation } @@ -1210,7 +1211,7 @@ object Types { case mt @ MethodType(_, formals) if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => val formals1 = if (dropLast == 0) formals else formals dropRight dropLast defn.FunctionOf( - formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) + formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType, mt.isImplicit && !ctx.erasedTypes) } /** The signature of this type. This is by default NotAMethod, @@ -1497,7 +1498,7 @@ object Types { (lastDefRunId != sym.defRunId) || (lastDefRunId == NoRunId) } || - (lastSymbol.infoOrCompleter == ErrorType || + (lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] || sym.owner != lastSymbol.owner && (sym.owner.derivesFrom(lastSymbol.owner) || selfTypeOf(sym).derivesFrom(lastSymbol.owner) || @@ -2376,7 +2377,9 @@ object Types { protected def computeSignature(implicit ctx: Context): Signature = resultSignature.prepend(paramTypes, isJava) - def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], resType: Type)(implicit ctx: Context) = + def derivedMethodType(paramNames: List[TermName] = this.paramNames, + paramTypes: List[Type] = this.paramTypes, + resType: Type = this.resType)(implicit ctx: Context) = if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this else { val resTypeFn = (x: MethodType) => resType.subst(this, x) @@ -2693,7 +2696,7 @@ object Types { protected def checkInst(implicit ctx: Context): this.type = { def check(tycon: Type): Unit = tycon.stripTypeVar match { case tycon: TypeRef if !tycon.symbol.isClass => - case _: PolyParam | ErrorType | _: WildcardType => + case _: PolyParam | _: ErrorType | _: WildcardType => case _: PolyType => assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") case tycon: AnnotatedType => @@ -3251,12 +3254,19 @@ object Types { override def computeHash = hashSeed } - abstract class ErrorType extends UncachedGroundType with ValueType + /** A common superclass of `ErrorType` and `TryDynamicCallSite`. Instances of this + * class are at the same time subtypes and supertypes of every other type. + */ + abstract class FlexType extends UncachedGroundType with ValueType + + class ErrorType(_msg: => Message) extends FlexType { + val msg = _msg + } - object ErrorType extends ErrorType + object UnspecifiedErrorType extends ErrorType("unspecified error") /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */ - object TryDynamicCallType extends ErrorType + object TryDynamicCallType extends FlexType /** Wildcard type, possibly with bounds */ abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType { @@ -3378,7 +3388,7 @@ object Types { /** Map this function over given type */ def mapOver(tp: Type): Type = { - implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type + implicit val ctx = this.ctx tp match { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 0f63b25bb..92ab10db9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -90,7 +90,8 @@ object JavaParsers { if (skipIt) skip() } - def errorTypeTree = TypeTree().withType(ErrorType) withPos Position(in.offset) + + def errorTypeTree = TypeTree().withType(UnspecifiedErrorType) withPos Position(in.offset) // --------- tree building ----------------------------- diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 8cc60c984..76f82d8af 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -146,6 +146,7 @@ object Parsers { def isNumericLit = numericLitTokens contains in.token def isModifier = modifierTokens contains in.token def isExprIntro = canStartExpressionTokens contains in.token + def isBindingIntro = canStartBindingTokens contains in.token def isTemplateIntro = templateIntroTokens contains in.token def isDclIntro = dclIntroTokens contains in.token def isStatSeqEnd = in.token == RBRACE || in.token == EOF @@ -597,7 +598,7 @@ object Parsers { val isNegated = negOffset < in.offset atPos(negOffset) { if (in.token == SYMBOLLIT) atPos(in.skipToken()) { SymbolLit(in.strVal) } - else if (in.token == INTERPOLATIONID) interpolatedString() + else if (in.token == INTERPOLATIONID) interpolatedString(inPattern) else finish(in.token match { case CHARLIT => in.charVal case INTLIT => in.intVal(isNegated).toInt @@ -621,10 +622,14 @@ object Parsers { in.nextToken() while (in.token == STRINGPART) { segmentBuf += Thicket( - literal(), + literal(inPattern = inPattern), atPos(in.offset) { if (in.token == IDENTIFIER) termIdent() + else if (in.token == USCORE && inPattern) { + in.nextToken() + Ident(nme.WILDCARD) + } else if (in.token == THIS) { in.nextToken() This(EmptyTypeIdent) @@ -633,12 +638,12 @@ object Parsers { if (inPattern) Block(Nil, inBraces(pattern())) else expr() else { - ctx.error(InterpolatedStringError()) + ctx.error(InterpolatedStringError(), source atPos Position(in.offset)) EmptyTree } }) } - if (in.token == STRINGLIT) segmentBuf += literal() + if (in.token == STRINGLIT) segmentBuf += literal(inPattern = inPattern) InterpolatedString(interpolator, segmentBuf.toList) } @@ -677,7 +682,7 @@ object Parsers { } } - /** Type ::= FunArgTypes `=>' Type + /** Type ::= [`implicit'] FunArgTypes `=>' Type * | HkTypeParamClause `->' Type * | InfixType * FunArgTypes ::= InfixType @@ -685,20 +690,26 @@ object Parsers { */ def typ(): Tree = { val start = in.offset + val isImplicit = in.token == IMPLICIT + if (isImplicit) in.nextToken() + def functionRest(params: List[Tree]): Tree = + atPos(start, accept(ARROW)) { + val t = typ() + if (isImplicit) new ImplicitFunction(params, t) else Function(params, t) + } val t = if (in.token == LPAREN) { in.nextToken() if (in.token == RPAREN) { in.nextToken() - atPos(start, accept(ARROW)) { Function(Nil, typ()) } + functionRest(Nil) } else { openParens.change(LPAREN, 1) val ts = commaSeparated(funArgType) openParens.change(LPAREN, -1) accept(RPAREN) - if (in.token == ARROW) - atPos(start, in.skipToken()) { Function(ts, typ()) } + if (isImplicit || in.token == ARROW) functionRest(ts) else { for (t <- ts) if (t.isInstanceOf[ByNameTypeTree]) @@ -718,7 +729,7 @@ object Parsers { else infixType() in.token match { - case ARROW => atPos(start, in.skipToken()) { Function(List(t), typ()) } + case ARROW => functionRest(t :: Nil) case FORSOME => syntaxError("existential types no longer supported; use a wildcard type or dependent type instead"); t case _ => t } @@ -941,14 +952,14 @@ object Parsers { } } - /** Expr ::= FunParams `=>' Expr + /** Expr ::= [`implicit'] FunParams `=>' Expr * | Expr1 * FunParams ::= Bindings - * | [`implicit'] Id + * | Id * | `_' * ExprInParens ::= PostfixExpr `:' Type * | Expr - * BlockResult ::= (FunParams | [`implicit'] Id `:' InfixType) => Block + * BlockResult ::= [`implicit'] FunParams `=>' Block * | Expr1 * Expr1 ::= `if' `(' Expr `)' {nl} Expr [[semi] else Expr] * | `if' Expr `then' Expr [[semi] else Expr] @@ -975,22 +986,27 @@ object Parsers { def expr(): Tree = expr(Location.ElseWhere) def expr(location: Location.Value): Tree = { - val saved = placeholderParams - placeholderParams = Nil - val t = expr1(location) - if (in.token == ARROW) { - placeholderParams = saved - closureRest(startOffset(t), location, convertToParams(t)) - } - else if (isWildcard(t)) { - placeholderParams = placeholderParams ::: saved - t + val start = in.offset + if (in.token == IMPLICIT) + implicitClosure(start, location, implicitMods()) + else { + val saved = placeholderParams + placeholderParams = Nil + val t = expr1(location) + if (in.token == ARROW) { + placeholderParams = saved + closureRest(start, location, convertToParams(t)) + } + else if (isWildcard(t)) { + placeholderParams = placeholderParams ::: saved + t + } + else + try + if (placeholderParams.isEmpty) t + else new WildcardFunction(placeholderParams.reverse, t) + finally placeholderParams = saved } - else - try - if (placeholderParams.isEmpty) t - else new WildcardFunction(placeholderParams.reverse, t) - finally placeholderParams = saved } def expr1(location: Location.Value = Location.ElseWhere): Tree = in.token match { @@ -1056,8 +1072,6 @@ object Parsers { atPos(in.skipToken()) { Return(if (isExprIntro) expr() else EmptyTree, EmptyTree) } case FOR => forExpr() - case IMPLICIT => - implicitClosure(in.skipToken(), location) case _ => expr1Rest(postfixExpr(), location) } @@ -1105,20 +1119,53 @@ object Parsers { } } + /** FunParams ::= Bindings + * | Id + * | `_' + * Bindings ::= `(' [Binding {`,' Binding}] `)' + */ + def funParams(mods: Modifiers, location: Location.Value): List[Tree] = + if (in.token == LPAREN) + inParens(if (in.token == RPAREN) Nil else commaSeparated(() => binding(mods))) + else { + val start = in.offset + val name = bindingName() + val t = + if (in.token == COLON && location == Location.InBlock) { + if (false) // Don't error yet, as the alternative syntax "implicit (x: T) => ... " + // is not supported by Scala2.x + migrationWarningOrError(s"This syntax is no longer supported; parameter needs to be enclosed in (...)") + + in.nextToken() + val t = infixType() + + if (false && in.isScala2Mode) { + patch(source, Position(start), "(") + patch(source, Position(in.lastOffset), ")") + } + t + } + else TypeTree() + (atPos(start) { makeParameter(name, t, mods) }) :: Nil + } + + /** Binding ::= (Id | `_') [`:' Type] + */ + def binding(mods: Modifiers): Tree = + atPos(in.offset) { makeParameter(bindingName(), typedOpt(), mods) } + + def bindingName(): TermName = + if (in.token == USCORE) { + in.nextToken() + ctx.freshName(nme.USCORE_PARAM_PREFIX).toTermName + } + else ident() + /** Expr ::= implicit Id `=>' Expr - * BlockResult ::= implicit Id [`:' InfixType] `=>' Block - */ - def implicitClosure(start: Int, location: Location.Value, implicitMod: Option[Mod] = None): Tree = { - var mods = atPos(start) { Modifiers(Implicit) } - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) - val id = termIdent() - val paramExpr = - if (location == Location.InBlock && in.token == COLON) - atPos(startOffset(id), in.skipToken()) { Typed(id, infixType()) } - else - id - closureRest(start, location, convertToParam(paramExpr, mods) :: Nil) - } + * BlockResult ::= implicit Id [`:' InfixType] `=>' Block // Scala2 only + */ + def implicitClosure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree = + closureRest(start, location, funParams(implicitMods, location)) def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree = atPos(start, in.offset) { @@ -1444,7 +1491,7 @@ object Parsers { case XMLSTART => xmlLiteralPattern() case _ => - if (isLiteral) literal() + if (isLiteral) literal(inPattern = true) else { syntaxErrorOrIncomplete(IllegalStartOfSimplePattern()) errorTermTree @@ -1479,7 +1526,7 @@ object Parsers { private def modOfToken(tok: Int): Mod = tok match { case ABSTRACT => Mod.Abstract() case FINAL => Mod.Final() - case IMPLICIT => Mod.Implicit(ImplicitCommon) + case IMPLICIT => Mod.Implicit() case INLINE => Mod.Inline() case LAZY => Mod.Lazy() case OVERRIDE => Mod.Override() @@ -1572,6 +1619,9 @@ object Parsers { normalize(loop(start)) } + def implicitMods(): Modifiers = + addMod(EmptyModifiers, atPos(accept(IMPLICIT)) { Mod.Implicit() }) + /** Wrap annotation or constructor in New(...).<init> */ def wrapNew(tpt: Tree) = Select(New(tpt), nme.CONSTRUCTOR) @@ -1677,9 +1727,9 @@ object Parsers { * Param ::= id `:' ParamType [`=' Expr] */ def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = { - var implicitMod: Mod = null - var firstClauseOfCaseClass = ofCaseClass + var imods: Modifiers = EmptyModifiers var implicitOffset = -1 // use once + var firstClauseOfCaseClass = ofCaseClass def param(): ValDef = { val start = in.offset var mods = annotsAsMods() @@ -1714,7 +1764,7 @@ object Parsers { if (in.token == ARROW) { if (owner.isTypeName && !(mods is Local)) syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name") - else if (implicitMod != null) + else if (imods.hasFlags) syntaxError("implicit parameters may not be call-by-name") } paramType() @@ -1726,7 +1776,7 @@ object Parsers { mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset))) implicitOffset = -1 } - if (implicitMod != null) mods = addMod(mods, implicitMod) + for (imod <- imods.mods) mods = addMod(mods, imod) ValDef(name, tpt, default).withMods(mods) } } @@ -1735,7 +1785,7 @@ object Parsers { else { if (in.token == IMPLICIT) { implicitOffset = in.offset - implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) } + imods = implicitMods() } commaSeparated(param) } @@ -1745,7 +1795,7 @@ object Parsers { if (in.token == LPAREN) paramClause() :: { firstClauseOfCaseClass = false - if (implicitMod == null) clauses() else Nil + if (imods.hasFlags) Nil else clauses() } else Nil } @@ -2210,9 +2260,9 @@ object Parsers { stats.toList } - def localDef(start: Int, implicitFlag: FlagSet, implicitMod: Option[Mod] = None): Tree = { - var mods = addFlag(defAnnotsMods(localModifierTokens), implicitFlag) - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) + def localDef(start: Int, implicitMods: Modifiers = EmptyModifiers): Tree = { + var mods = defAnnotsMods(localModifierTokens) + for (imod <- implicitMods.mods) mods = addMod(mods, imod) defOrDcl(start, mods) } @@ -2235,11 +2285,11 @@ object Parsers { else if (isDefIntro(localModifierTokens)) if (in.token == IMPLICIT) { val start = in.offset - val mod = atPos(in.skipToken()) { Mod.Implicit(ImplicitCommon) } - if (isIdent) stats += implicitClosure(start, Location.InBlock, Some(mod)) - else stats += localDef(start, ImplicitCommon, Some(mod)) + val imods = implicitMods() + if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) + else stats += localDef(start, imods) } else { - stats += localDef(in.offset, EmptyFlags) + stats += localDef(in.offset) } else if (!isStatSep && (in.token != CASE)) { exitOnError = mustStartStat diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 60003d098..101be167e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -758,7 +758,7 @@ object Scanners { finishStringPart() nextRawChar() next.token = LBRACE - } else if (Character.isUnicodeIdentifierStart(ch)) { + } else if (Character.isUnicodeIdentifierStart(ch) || ch == '_') { finishStringPart() do { putChar(ch) diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 5324207db..280832ef3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -209,6 +209,8 @@ object Tokens extends TokensCommon { final val canStartTypeTokens = literalTokens | identifierTokens | BitSet( THIS, SUPER, USCORE, LPAREN, AT) + final val canStartBindingTokens = identifierTokens | BitSet(USCORE, LPAREN) + final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT) final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index b321d3736..05f1af9d7 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -96,7 +96,10 @@ object Formatting { case tpe: Type => tpe.exists && !tpe.isErroneous case sym: Symbol if sym.isCompleted => - sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists + sym.info match { + case _: ErrorType | TypeAlias(_: ErrorType) | NoType => false + case _ => true + } case _ => true } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 15c382bb0..61f23c214 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -154,7 +154,7 @@ class PlainPrinter(_ctx: Context) extends Printer { changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } case OrType(tp1, tp2) => changePrec(OrPrec) { toText(tp1) ~ " | " ~ toText(tp2) } - case ErrorType => + case _: ErrorType => "<error>" case tp: WildcardType => if (tp.optBounds.exists) "(?" ~ toTextRHS(tp.bounds) ~ ")" else "?" diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 1ddf3cd6d..3085ad8fd 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -113,20 +113,21 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def toText(tp: Type): Text = controlled { def toTextTuple(args: List[Type]): Text = "(" ~ Text(args.map(argText), ", ") ~ ")" - def toTextFunction(args: List[Type]): Text = + def toTextFunction(args: List[Type], isImplicit: Boolean): Text = changePrec(GlobalPrec) { val argStr: Text = if (args.length == 2 && !defn.isTupleType(args.head)) atPrec(InfixPrec) { argText(args.head) } else toTextTuple(args.init) - argStr ~ " => " ~ argText(args.last) + ("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last) } homogenize(tp) match { case AppliedType(tycon, args) => val cls = tycon.typeSymbol if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" - if (defn.isFunctionClass(cls)) return toTextFunction(args) + if (defn.isFunctionClass(cls)) return toTextFunction(args, isImplicit = false) + if (defn.isImplicitFunctionClass(cls)) return toTextFunction(args, isImplicit = true) if (defn.isTupleClass(cls)) return toTextTuple(args) return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close case tp: TypeRef => diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala index 7fd50bfdc..c27644ad9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala @@ -14,7 +14,7 @@ object MessageContainer { implicit class MessageContext(val c: Context) extends AnyVal { def shouldExplain(cont: MessageContainer): Boolean = { - implicit val ctx: Context = c + implicit val ctx = c cont.contained.explanation match { case "" => false case _ => ctx.settings.explain.value diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 489165e56..9dda233bf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -224,6 +224,8 @@ object messages { extends Message(8) { val kind = "Member Not Found" + //println(i"site = $site, decls = ${site.decls}, source = ${site.widen.typeSymbol.sourceFile}") //DEBUG + val msg = { import core.Flags._ val maxDist = 3 @@ -606,7 +608,7 @@ object messages { |""" } - case class WrongNumberOfArgs(fntpe: Type, argKind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) + case class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) extends Message(22) { val kind = "Syntax" @@ -628,7 +630,7 @@ object messages { } val msg = - hl"""|${NoColor(msgPrefix)} ${argKind} arguments for $prettyName$expectedArgString + hl"""|${NoColor(msgPrefix)} type arguments for $prettyName$expectedArgString |expected: $expectedArgString |actual: $actualArgString""".stripMargin diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index bc8528c05..1fffe6841 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -3,7 +3,7 @@ package sbt import ast.{Trees, tpd} import core._, core.Decorators._ -import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ +import Annotations._, Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ import Names._, NameOps._, StdNames._ import typer.Inliner @@ -333,7 +333,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // TODO: Never dealias. We currently have to dealias because // sbt main class discovery relies on the signature of the main // method being fully dealiased. See https://github.com/sbt/zinc/issues/102 - val tp2 = if (!tp.isHK) tp.dealias else tp + val tp2 = if (!tp.isHK) tp.dealiasKeepAnnots else tp tp2 match { case NoPrefix | NoType => Constants.emptyType @@ -411,9 +411,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder case ConstantType(constant) => new api.Constant(apiType(constant.tpe), constant.stringValue) case AnnotatedType(tpe, annot) => - // TODO: Annotation support - ctx.debuglog(i"sbt-api: skipped annotation in $tp2") - apiType(tpe) + new api.Annotated(apiType(tpe), Array(apiAnnotation(annot))) case tp: ThisType => apiThis(tp.cls) case tp: ParamType => @@ -498,7 +496,6 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder sym.is(Implicit), sym.is(Lazy), sym.is(Macro), sym.is(SuperAccessor)) } - // TODO: Support other annotations def apiAnnotations(s: Symbol): List[api.Annotation] = { val annots = new mutable.ListBuffer[api.Annotation] @@ -513,6 +510,27 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder annots += marker(Inliner.bodyToInline(s).show(printTypesCtx).toString) } + // In the Scala2 ExtractAPI phase we only extract annotations that extend + // StaticAnnotation, but in Dotty we currently pickle all annotations so we + // extract everything (except inline body annotations which are handled + // above). + s.annotations.filter(_.symbol != defn.BodyAnnot) foreach { annot => + annots += apiAnnotation(annot) + } + annots.toList } + + def apiAnnotation(annot: Annotation): api.Annotation = { + // FIXME: To faithfully extract an API we should extract the annotation tree, + // sbt instead wants us to extract the annotation type and its arguments, + // to do this properly we would need a way to hash trees and types in dotty itself, + // instead we pretty-print the annotation tree. + // However, we still need to extract the annotation type in the way sbt expect + // because sbt uses this information to find tests to run (for example + // junit tests are annotated @org.junit.Test). + new api.Annotation( + apiType(annot.tree.tpe), // Used by sbt to find tests to run + Array(new api.AnnotationArgument("FULLTREE", annot.tree.show.toString))) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 5c880c7bd..71ecb5c65 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -345,21 +345,23 @@ object Erasure extends TypeTestsCasts{ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { def mapOwner(sym: Symbol): Symbol = { - val owner = sym.owner - if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) { - assert(sym.isConstructor, s"${sym.showLocated}") - defn.ObjectClass - } - else if (defn.isUnimplementedFunctionClass(owner)) - defn.FunctionXXLClass - else - owner + def recur(owner: Symbol): Symbol = + if ((owner eq defn.AnyClass) || (owner eq defn.AnyValClass)) { + assert(sym.isConstructor, s"${sym.showLocated}") + defn.ObjectClass + } else if (defn.isUnimplementedFunctionClass(owner)) + defn.FunctionXXLClass + else if (defn.isImplicitFunctionClass(owner)) + recur(defn.FunctionClass(owner.name.functionArity)) + else + owner + recur(sym.owner) } - var sym = tree.symbol - val owner = mapOwner(sym) - if (owner ne sym.owner) sym = owner.info.decl(sym.name).symbol - assert(sym.exists, owner) + val origSym = tree.symbol + val owner = mapOwner(origSym) + val sym = if (owner eq origSym.owner) origSym else owner.info.decl(origSym.name).symbol + assert(sym.exists, origSym.showLocated) def select(qual: Tree, sym: Symbol): Tree = { val name = tree.typeOpt match { @@ -575,7 +577,7 @@ object Erasure extends TypeTestsCasts{ val bridge = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Flags.Synthetic | Flags.Method, sam.info) val bridgeCtx = ctx.withOwner(bridge) Closure(bridge, bridgeParamss => { - implicit val ctx: Context = bridgeCtx + implicit val ctx = bridgeCtx val List(bridgeParams) = bridgeParamss val rhs = Apply(meth, (bridgeParams, implParamTypes).zipped.map(adapt(_, _))) @@ -691,7 +693,7 @@ object Erasure extends TypeTestsCasts{ val bridgeCtx = ctx.withOwner(bridge) tpd.DefDef(bridge, { paramss: List[List[tpd.Tree]] => - implicit val ctx: Context = bridgeCtx + implicit val ctx = bridgeCtx val rhs = paramss.foldLeft(sel)((fun, vparams) => fun.tpe.widen match { diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index a32e1c921..5463d5080 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -95,7 +95,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf if (needsOuterIfReferenced(parentTrait)) { val parentTp = cls.denot.thisType.baseTypeRef(parentTrait) val outerAccImpl = newOuterAccessor(cls, parentTrait).enteredAfter(thisTransformer) - newDefs += DefDef(outerAccImpl, singleton(outerPrefix(parentTp))) + newDefs += DefDef(outerAccImpl, singleton(fixThis(outerPrefix(parentTp)))) } } @@ -147,7 +147,8 @@ object ExplicitOuter { private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = { val target = cls.owner.enclosingClass.typeRef val info = if (flags.is(Method)) ExprType(target) else target - ctx.newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) + ctx.withPhaseNoEarlier(ctx.explicitOuterPhase.next) // outer accessors are entered at explicitOuter + 1, should not be defined before. + .newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) } /** A new param accessor for the outer field in class `cls` */ @@ -275,6 +276,25 @@ object ExplicitOuter { outerPrefix(tpe.underlying) } + /** It's possible (i1755.scala gives an example) that the type + * given by outerPrefix contains a This-reference to a module outside + * the context where that module is defined. This needs to be translated + * to an access to the module object from the enclosing class or object. + * + * This solution is a bit of a hack; it would be better to avoid + * such references to the This of a module from outside the module + * in the first place. I was not yet able to find out how such references + * arise and how to avoid them. + */ + private def fixThis(tpe: Type)(implicit ctx: Context): Type = tpe match { + case tpe: ThisType if tpe.cls.is(Module) && !ctx.owner.isContainedIn(tpe.cls) => + fixThis(TermRef(tpe.cls.owner.thisType, tpe.cls.sourceModule.asTerm)) + case tpe: TermRef => + tpe.derivedSelect(fixThis(tpe.prefix)) + case _ => + tpe + } + def outer(implicit ctx: Context): OuterOps = new OuterOps(ctx) /** The operations in this class @@ -313,7 +333,7 @@ object ExplicitOuter { val cls = fun.symbol.owner.asClass def outerArg(receiver: Tree): Tree = receiver match { case New(_) | Super(_, _) => - singleton(outerPrefix(receiver.tpe)) + singleton(fixThis(outerPrefix(receiver.tpe))) case This(_) => ref(outerParamAccessor(cls)) // will be rewired to outer argument of secondary constructor in phase Constructors case TypeApply(Select(r, nme.asInstanceOf_), args) => diff --git a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 5ae4e8a54..925ec08b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -135,14 +135,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful // TODO: this is state and should be per-run // todo: check that when transformation finished map is empty - private def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Unit = - if (seen contains clazz) - ctx.error("value class may not unbox to itself", pos) - else { - val unboxed = underlyingOfValueClass(clazz).typeSymbol - if (isDerivedValueClass(unboxed)) checkNonCyclic(pos, seen + clazz, unboxed.asClass) - } - override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { if (isDerivedValueClass(ctx.owner)) { /* This is currently redundant since value classes may not diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index e63a7c3a7..a6ac71286 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -141,7 +141,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos) val result = ref(holderSymbol).select(lazyNme.value) val flag = ref(holderSymbol).select(lazyNme.initialized) - val initer = valueInitter.changeOwner(x.symbol, initSymbol) + val initer = valueInitter.changeOwnerAfter(x.symbol, initSymbol, this) val initBody = adaptToType( ref(holderSymbol).select(defn.Object_synchronized).appliedTo( diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 3e25cf82e..181dfccd9 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -235,14 +235,21 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { // next: MatchMonad[U] // returns MatchMonad[U] def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - - val getTp = extractorMemberType(prev.tpe, nme.get) - val isDefined = extractorMemberType(prev.tpe, nme.isDefined) - - if ((isDefined isRef defn.BooleanClass) && getTp.exists) { - // isDefined and get may be overloaded - val getDenot = prev.tpe.member(nme.get).suchThat(_.info.isParameterless) - val isDefinedDenot = prev.tpe.member(nme.isDefined).suchThat(_.info.isParameterless) + val resultArity = defn.productArity(b.info) + if (isProductMatch(prev.tpe, resultArity)) { + val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null))) + ifThenElseZero( + nullCheck, + Block( + List(ValDef(b.asTerm, prev)), + next //Substitution(b, ref(prevSym))(next) + ) + ) + } + else { + val getDenot = extractorMember(prev.tpe, nme.get) + val isEmptyDenot = extractorMember(prev.tpe, nme.isEmpty) + assert(getDenot.exists && isEmptyDenot.exists, i"${prev.tpe}") val tmpSym = freshSym(prev.pos, prev.tpe, "o") val prevValue = ref(tmpSym).select(getDenot.symbol).ensureApplied @@ -251,20 +258,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { List(ValDef(tmpSym, prev)), // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) ifThenElseZero( - ref(tmpSym).select(isDefinedDenot.symbol), + ref(tmpSym).select(isEmptyDenot.symbol).select(defn.Boolean_!), Block(List(ValDef(b.asTerm, prevValue)), next) ) ) - } else { - assert(defn.isProductSubType(prev.tpe)) - val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null))) - ifThenElseZero( - nullCheck, - Block( - List(ValDef(b.asTerm, prev)), - next //Substitution(b, ref(prevSym))(next) - ) - ) } } @@ -1431,12 +1428,12 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { case _ => or } - def resultInMonad = if (aligner.isBool) defn.UnitType else { - val getTp = extractorMemberType(resultType, nme.get) - if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && getTp.exists) - getTp + def resultInMonad = + if (aligner.isBool) defn.UnitType + else if (isProductMatch(resultType, aligner.prodArity)) resultType + else if (isGetMatch(resultType)) extractorMemberType(resultType, nme.get) else resultType - } + def resultType: Type /** Create the TreeMaker that embodies this extractor call @@ -1632,13 +1629,12 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { //val spr = subPatRefs(binder) assert(go && go1) ref(binder) :: Nil - } else { - lazy val getTp = extractorMemberType(binderTypeTested, nme.get) - if ((aligner.isSingle && aligner.extractor.prodArity == 1) && ((extractorMemberType(binderTypeTested, nme.isDefined) isRef defn.BooleanClass) && getTp.exists)) - List(ref(binder)) - else - subPatRefs(binder) } + else if ((aligner.isSingle && aligner.extractor.prodArity == 1) && + !isProductMatch(binderTypeTested, aligner.prodArity) && isGetMatch(binderTypeTested)) + List(ref(binder)) + else + subPatRefs(binder) } /*protected def spliceApply(binder: Symbol): Tree = { @@ -1890,9 +1886,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { else if (result.classSymbol is Flags.CaseClass) result.decls.filter(x => x.is(Flags.CaseAccessor) && x.is(Flags.Method)).map(_.info).toList else result.select(nme.get) :: Nil )*/ - if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && resultOfGet.exists) - getUnapplySelectors(resultOfGet, args) - else if (defn.isProductSubType(resultType)) productSelectorTypes(resultType) + if (isProductMatch(resultType, args.length)) productSelectorTypes(resultType) + else if (isGetMatch(resultType)) getUnapplySelectors(resultOfGet, args) else if (resultType isRef defn.BooleanClass) Nil else { ctx.error(i"invalid return type in Unapply node: $resultType") diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala new file mode 100644 index 000000000..b5469610f --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -0,0 +1,165 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers.IdentityDenotTransformer +import core.Symbols._ +import core.Contexts._ +import core.Types._ +import core.Flags._ +import core.Decorators._ +import core.StdNames.nme +import core.Names._ +import core.NameOps._ +import ast.Trees._ +import ast.tpd +import collection.mutable + +/** This phase optimizes code using implicit function types, by applying two rewrite rules. + * Let IF be the implicit function type + * + * implicit Us => R + * + * (1) A method definition + * + * def m(xs: Ts): IF = implicit (ys: Us) => E + * + * is expanded to two methods: + * + * def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys) + * def m$direct(xs: Ts)(ys: Us): R = E + * + * (and equivalently for methods with type parameters or a different number of value parameter lists). + * An abstract method definition + * + * def m(xs: Ts): IF + * + * is expanded to: + * + * def m(xs: Ts): IF + * def m$direct(xs: Ts)(ys: Us): R + * + * (2) A reference `qual.apply` where `qual` has implicit function type and + * `qual` refers to a method `m` is rewritten to a reference to `m$direct`, + * keeping the same type and value arguments as they are found in `qual`. + */ +class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTransform => + import tpd._ + + override def phaseName: String = "shortcutImplicits" + val treeTransform = new Transform + + /** If this option is true, we don't specialize symbols that are known to be only + * targets of monomorphic calls. + * The reason for this option is that benchmarks show that on the JVM for monomorphic dispatch + * scenarios inlining and escape analysis can often remove all calling overhead, so we might as + * well not duplicate the code. We need more experience to decide on the best setting of this option. + */ + final val specializeMonoTargets = true + + class Transform extends TreeTransform { + def phase = thisTransform + + override def prepareForUnit(tree: Tree)(implicit ctx: Context) = new Transform + + /** A map to cache mapping local methods to their direct counterparts. + * A fresh map is created for each unit. + */ + private val directMeth = new mutable.HashMap[Symbol, Symbol] + + /** Should `sym` get a ..$direct companion? + * This is the case if (1) `sym` is a method with an implicit function type as final result type. + * However if `specializeMonoTargets` is false, we exclude symbols that are known + * to be only targets of monomorphic calls because they are effectively + * final and don't override anything. + */ + private def shouldBeSpecialized(sym: Symbol)(implicit ctx: Context) = + sym.is(Method, butNot = Accessor) && + defn.isImplicitFunctionType(sym.info.finalResultType) && + (specializeMonoTargets || !sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty) + + /** @pre The type's final result type is an implicit function type `implicit Ts => R`. + * @return The type of the `apply` member of `implicit Ts => R`. + */ + private def directInfo(info: Type)(implicit ctx: Context): Type = info match { + case info: PolyType => info.derivedPolyType(resType = directInfo(info.resultType)) + case info: MethodType => info.derivedMethodType(resType = directInfo(info.resultType)) + case info: ExprType => directInfo(info.resultType) + case info => info.member(nme.apply).info + } + + /** A new `m$direct` method to accompany the given method `m` */ + private def newDirectMethod(sym: Symbol)(implicit ctx: Context): Symbol = { + val direct = sym.copy( + name = sym.name.directName, + flags = sym.flags | Synthetic, + info = directInfo(sym.info)) + if (direct.allOverriddenSymbols.isEmpty) direct.resetFlag(Override) + direct + } + + /** The direct method `m$direct` that accompanies the given method `m`. + * Create one if it does not exist already. + */ + private def directMethod(sym: Symbol)(implicit ctx: Context): Symbol = + if (sym.owner.isClass) { + val direct = sym.owner.info.member(sym.name.directName) + .suchThat(_.info matches directInfo(sym.info)).symbol + if (direct.maybeOwner == sym.owner) direct + else newDirectMethod(sym).enteredAfter(thisTransform) + } + else directMeth.getOrElseUpdate(sym, newDirectMethod(sym)) + + + /** Transform `qual.apply` occurrences according to rewrite rule (2) above */ + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = + if (tree.name == nme.apply && + defn.isImplicitFunctionType(tree.qualifier.tpe.widen) && + shouldBeSpecialized(tree.qualifier.symbol)) { + def directQual(tree: Tree): Tree = tree match { + case Apply(fn, args) => cpy.Apply(tree)(directQual(fn), args) + case TypeApply(fn, args) => cpy.TypeApply(tree)(directQual(fn), args) + case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr)) + case tree: RefTree => + cpy.Ref(tree)(tree.name.directName) + .withType(directMethod(tree.symbol).termRef) + } + directQual(tree.qualifier) + } else tree + + /** Transform methods with implicit function type result according to rewrite rule (1) above */ + override def transformDefDef(mdef: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val original = mdef.symbol + if (shouldBeSpecialized(original)) { + val direct = directMethod(original) + + def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match { + case Block(Nil, expr) => splitClosure(expr) + case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) => + val tparamSyms = mdef.tparams.map(_.symbol) + val vparamSymss = mdef.vparamss.map(_.map(_.symbol)) + val clparamSyms = clparams.map(_.symbol) + val remappedCore = (ts: List[Type]) => (prefss: List[List[Tree]]) => + meth.rhs + .subst(tparamSyms ::: (vparamSymss.flatten ++ clparamSyms), + ts.map(_.typeSymbol) ::: prefss.flatten.map(_.symbol)) + .changeOwnerAfter(original, direct, thisTransform) + .changeOwnerAfter(meth.symbol, direct, thisTransform) + val forwarder = ref(direct) + .appliedToTypeTrees(tparamSyms.map(ref(_))) + .appliedToArgss(vparamSymss.map(_.map(ref(_))) :+ clparamSyms.map(ref(_))) + val fwdClosure = cpy.Block(tree)(cpy.DefDef(meth)(rhs = forwarder) :: Nil, cl) + (remappedCore, fwdClosure) + case EmptyTree => + (_ => _ => EmptyTree, EmptyTree) + } + + val (remappedCore, fwdClosure) = splitClosure(mdef.rhs) + val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure) + val directDef = polyDefDef(direct.asTerm, remappedCore) + Thicket(originalDef, directDef) + } + else mdef + } + } +} diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index aa4eefe43..eee429a87 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -129,17 +129,20 @@ class TreeChecker extends Phase with SymTransformer { try checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) catch { case NonFatal(ex) => //TODO CHECK. Check that we are bootstrapped - implicit val ctx: Context = checkingCtx + implicit val ctx = checkingCtx println(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***") throw ex } } - class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { + class Checker(phasesToCheck: Seq[Phase]) extends ReTyper with Checking { val nowDefinedSyms = new mutable.HashSet[Symbol] val everDefinedSyms = new mutable.HashMap[Symbol, Tree] + // don't check value classes after typer, as the constraint about constructors doesn't hold after transform + override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () + def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { case tree: DefTree => val sym = tree.symbol diff --git a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala index 5385ca720..b0bd40578 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -3,7 +3,6 @@ package dotc package transform import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Annotations.ConcreteAnnotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, DenotTransformer} import dotty.tools.dotc.core.Denotations.SingleDenotation @@ -181,10 +180,15 @@ object TreeTransforms { abstract override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = super.transform(ref) match { case ref1: SymDenotation if ref1.symbol.isDefinedInCurrentRun => - val annotTrees = ref1.annotations.map(_.tree) + val annots = ref1.annotations + val annotTrees = annots.map(_.tree) val annotTrees1 = annotTrees.mapConserve(annotationTransformer.macroTransform) if (annotTrees eq annotTrees1) ref1 - else ref1.copySymDenotation(annotations = annotTrees1.map(new ConcreteAnnotation(_))) + else { + val derivedAnnots = (annots, annotTrees1).zipped.map((annot, annotTree1) => + annot.derivedAnnotation(annotTree1)) + ref1.copySymDenotation(annotations = derivedAnnots) + } case ref1 => ref1 } diff --git a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala index 93005c57a..b16d05644 100644 --- a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -53,4 +53,14 @@ object ValueClasses { def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type = valueClassUnbox(d).info.resultType + /** Whether a value class wraps itself */ + def isCyclic(cls: ClassSymbol)(implicit ctx: Context): Boolean = { + def recur(seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Boolean = + (seen contains clazz) || { + val unboxed = underlyingOfValueClass(clazz).typeSymbol + (isDerivedValueClass(unboxed)) && recur(seen + clazz, unboxed.asClass) + } + + recur(Set[Symbol](), cls) + } } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 11121e1f3..8a18e63c0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -32,16 +32,37 @@ import reporting.diagnostic.Message object Applications { import tpd._ + def extractorMember(tp: Type, name: Name)(implicit ctx: Context) = { + def isPossibleExtractorType(tp: Type) = tp match { + case _: MethodType | _: PolyType => false + case _ => true + } + tp.member(name).suchThat(d => isPossibleExtractorType(d.info)) + } + def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx: Context) = { - val ref = tp.member(name).suchThat(_.info.isParameterless) + val ref = extractorMember(tp, name) if (ref.isOverloaded) errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos) - else if (ref.info.isInstanceOf[PolyType]) - errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", errorPos) - else - ref.info.widenExpr.dealias + ref.info.widenExpr.dealias } + /** Does `tp` fit the "product match" conditions as an unapply result type + * for a pattern with `numArgs` subpatterns> + * This is the case of `tp` is a subtype of the Product<numArgs> class. + */ + def isProductMatch(tp: Type, numArgs: Int)(implicit ctx: Context) = + 0 <= numArgs && numArgs <= Definitions.MaxTupleArity && + tp.derivesFrom(defn.ProductNType(numArgs).typeSymbol) + + /** Does `tp` fit the "get match" conditions as an unapply result type? + * This is the case of `tp` has a `get` member as well as a + * parameterless `isDefined` member of result type `Boolean`. + */ + def isGetMatch(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context) = + extractorMemberType(tp, nme.isEmpty, errorPos).isRef(defn.BooleanClass) && + extractorMemberType(tp, nme.get, errorPos).exists + def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = { val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) sels.takeWhile(_.exists).toList @@ -61,24 +82,37 @@ object Applications { def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] = { + val unapplyName = unapplyFn.symbol.name def seqSelector = defn.RepeatedParamType.appliedTo(unapplyResult.elemType :: Nil) def getTp = extractorMemberType(unapplyResult, nme.get, pos) - // println(s"unapply $unapplyResult ${extractorMemberType(unapplyResult, nme.isDefined)}") - if (extractorMemberType(unapplyResult, nme.isDefined, pos) isRef defn.BooleanClass) { - if (getTp.exists) - if (unapplyFn.symbol.name == nme.unapplySeq) { - val seqArg = boundsToHi(getTp.elemType) - if (seqArg.exists) return args map Function.const(seqArg) - } - else return getUnapplySelectors(getTp, args, pos) - else if (defn.isProductSubType(unapplyResult)) return productSelectorTypes(unapplyResult, pos) + def fail = { + ctx.error(i"$unapplyResult is not a valid result type of an $unapplyName method of an extractor", pos) + Nil + } + + if (unapplyName == nme.unapplySeq) { + if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil + else if (isGetMatch(unapplyResult, pos)) { + val seqArg = boundsToHi(getTp.elemType) + if (seqArg.exists) args.map(Function.const(seqArg)) + else fail + } + else fail } - if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil - else if (unapplyResult isRef defn.BooleanClass) Nil else { - ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", pos) - Nil + assert(unapplyName == nme.unapply) + if (isProductMatch(unapplyResult, args.length)) + productSelectorTypes(unapplyResult) + else if (isGetMatch(unapplyResult, pos)) + getUnapplySelectors(getTp, args, pos) + else if (unapplyResult isRef defn.BooleanClass) + Nil + else if (defn.isProductSubType(unapplyResult)) + productSelectorTypes(unapplyResult) + // this will cause a "wrong number of arguments in pattern" error later on, + // which is better than the message in `fail`. + else fail } } @@ -527,7 +561,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => var typedArgs = typedArgBuf.toList def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = - if (!success) app0.withType(ErrorType) + if (!success) app0.withType(UnspecifiedErrorType) else { if (!sameSeq(args, orderedArgs)) { // need to lift arguments to maintain evaluation order in the @@ -620,7 +654,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } fun1.tpe match { - case ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(ErrorType) + case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err) case TryDynamicCallType => typedDynamicApply(tree, pt) case _ => tryEither { @@ -884,7 +918,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case tp => val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) - cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType + cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType unapplyErr.tpe } } @@ -941,9 +975,21 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } /** In a set of overloaded applicable alternatives, is `alt1` at least as good as - * `alt2`? `alt1` and `alt2` are non-overloaded references. + * `alt2`? Also used for implicits disambiguation. + * + * @param alt1, alt2 Non-overloaded references indicating the two choices + * @param level1, level2 If alternatives come from a comparison of two contextual + * implicit candidates, the nesting levels of the candidates. + * In all other cases the nesting levels are both 0. + * + * An alternative A1 is "as good as" an alternative A2 if it wins or draws in a tournament + * that awards one point for each of the following + * + * - A1 is nested more deeply than A2 + * - The nesting levels of A1 and A2 are the same, and A1's owner derives from A2's owner + * - A1's type is more specific than A2's type. */ - def isAsGood(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Boolean = track("isAsGood") { ctx.traceIndented(i"isAsGood($alt1, $alt2)", overload) { + def isAsGood(alt1: TermRef, alt2: TermRef, nesting1: Int = 0, nesting2: Int = 0)(implicit ctx: Context): Boolean = track("isAsGood") { ctx.traceIndented(i"isAsGood($alt1, $alt2)", overload) { assert(alt1 ne alt2) @@ -992,7 +1038,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val nestedCtx = ctx.fresh.setExploreTyperState { - implicit val ctx: Context = nestedCtx + implicit val ctx = nestedCtx isAsSpecificValueType(tp1, constrained(tp2).resultType) } case _ => // (3b) @@ -1058,9 +1104,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val tp1 = stripImplicit(alt1.widen) val tp2 = stripImplicit(alt2.widen) - def winsOwner1 = isDerived(owner1, owner2) + def winsOwner1 = nesting1 > nesting2 || isDerived(owner1, owner2) def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2) - def winsOwner2 = isDerived(owner2, owner1) + def winsOwner2 = nesting2 > nesting1 || isDerived(owner2, owner1) def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1) overload.println(i"isAsGood($alt1, $alt2)? $tp1 $tp2 $winsOwner1 $winsType1 $winsOwner2 $winsType2") @@ -1260,7 +1306,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val alts1 = alts filter pt.isMatchedBy resolveOverloaded(alts1, pt1, targs1) - case defn.FunctionOf(args, resultType) => + case defn.FunctionOf(args, resultType, _) => narrowByTypes(alts, args, resultType) case pt => @@ -1311,7 +1357,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // (p_1_1, ..., p_m_1) => r_1 // ... // (p_1_n, ..., p_m_n) => r_n - val decomposedFormalsForArg: List[Option[(List[Type], Type)]] = + val decomposedFormalsForArg: List[Option[(List[Type], Type, Boolean)]] = formalsForArg.map(defn.FunctionOf.unapply) if (decomposedFormalsForArg.forall(_.isDefined)) { val formalParamTypessForArg: List[List[Type]] = diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index d80dfe7c0..41d9f9572 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -29,6 +29,7 @@ import ErrorReporting.{err, errorType} import config.Printers.typr import collection.mutable import SymDenotations.NoCompleter +import dotty.tools.dotc.transform.ValueClasses._ object Checking { import tpd._ @@ -55,8 +56,8 @@ object Checking { def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - /** Check applied type trees for well-formedness. This means - * - all arguments are within their corresponding bounds + /** Check applied type trees for well-formedness. This means + * - all arguments are within their corresponding bounds * - if type is a higher-kinded application with wildcard arguments, * check that it or one of its supertypes can be reduced to a normal application. * Unreducible applications correspond to general existentials, and we @@ -88,12 +89,12 @@ object Checking { checkWildcardHKApply(tp.superType, pos) } case _ => - } + } def checkValidIfHKApply(implicit ctx: Context): Unit = checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) } - + /** Check that `tp` refers to a nonAbstract class * and that the instance conforms to the self type of the created class. */ @@ -236,8 +237,7 @@ object Checking { catch { case ex: CyclicReference => if (reportErrors) { - ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) - ErrorType + errorType(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) } else info } @@ -406,6 +406,43 @@ object Checking { notPrivate.errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) } info } + + /** Verify classes extending AnyVal meet the requirements */ + def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = { + def checkValueClassMember(stat: Tree) = stat match { + case _: ValDef if !stat.symbol.is(ParamAccessor) => + ctx.error(s"value class may not define non-parameter field", stat.pos) + case d: DefDef if d.symbol.isConstructor => + ctx.error(s"value class may not define secondary constructor", stat.pos) + case _: MemberDef | _: Import | EmptyTree => + // ok + case _ => + ctx.error(s"value class may not contain initialization statements", stat.pos) + } + if (isDerivedValueClass(clazz)) { + if (clazz.is(Trait)) + ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos) + if (clazz.is(Abstract)) + ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos) + if (!clazz.isStatic) + ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos) + if (isCyclic(clazz.asClass)) + ctx.error("value class cannot wrap itself", clazz.pos) + else { + val clParamAccessors = clazz.asClass.paramAccessors.filter(_.isTerm) + clParamAccessors match { + case List(param) => + if (param.is(Mutable)) + ctx.error("value class parameter must not be a var", param.pos) + + case _ => + ctx.error("value class needs to have exactly one val parameter", clazz.pos) + } + } + stats.foreach(checkValueClassMember) + } + + } } trait Checking { @@ -553,6 +590,10 @@ trait Checking { errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where") } else tpt + + /** Verify classes extending AnyVal meet the requirements */ + def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = + Checking.checkDerivedValueClass(clazz, stats) } trait NoChecking extends Checking { @@ -568,4 +609,5 @@ trait NoChecking extends Checking { override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt + override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () } diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index b5ace87d3..4039c8b81 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Decorators._ +import ErrorReporting._ object Dynamic { def isDynamicMethod(name: Name): Boolean = @@ -41,10 +42,9 @@ trait Dynamic { self: Typer with Applications => def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } val args = tree.args val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic - if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) { - ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos) - tree.withType(ErrorType) - } else { + if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) + errorTree(tree, "applyDynamicNamed does not support passing a vararg parameter") + else { def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg)) def namedArgs = args.map { case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg) @@ -89,8 +89,7 @@ trait Dynamic { self: Typer with Applications => case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => typedDynamicAssign(qual, name, targs) case _ => - ctx.error("reassignment to val", tree.pos) - tree.withType(ErrorType) + errorTree(tree, "reassignment to val") } } diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index a18c83ff8..1238ad568 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -23,7 +23,7 @@ object ErrorReporting { def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = { ctx.error(msg, pos) - ErrorType + new ErrorType(msg) } def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = { @@ -46,11 +46,17 @@ object ErrorReporting { errorMsg(msg, cx.outer) } } else msg - errorMsg(ex.show, ctx) + + if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm) + em"""cyclic reference involving implicit $cycleSym + |This happens when the right hand-side of $cycleSym's definition involves an implicit search. + |To avoid the error, give $cycleSym an explicit type.""" + else + errorMsg(ex.show, ctx) } - def wrongNumberOfArgs(fntpe: Type, kind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = - errorType(WrongNumberOfArgs(fntpe, kind, expectedArgs, actual)(ctx), pos) + def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = + errorType(WrongNumberOfTypeArgs(fntpe, expectedArgs, actual)(ctx), pos) class Errors(implicit ctx: Context) { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1a9a8f64c..331533204 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -29,12 +29,15 @@ import Inferencing.fullyDefinedType import Trees._ import Hashable._ import config.Config -import config.Printers.{implicits, implicitsDetailed} +import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable /** Implicit resolution */ object Implicits { + /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ + case class Candidate(ref: TermRef, level: Int) + /** A common base class of contextual implicits and of-type implicits which * represents a set of implicit references. */ @@ -42,11 +45,14 @@ object Implicits { implicit val ctx: Context = if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled + /** The nesting level of this context. Non-zero only in ContextialImplicits */ + def level: Int = 0 + /** The implicit references */ def refs: List[TermRef] /** Return those references in `refs` that are compatible with type `pt`. */ - protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") { + protected def filterMatching(pt: Type)(implicit ctx: Context): List[Candidate] = track("filterMatching") { def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ { @@ -97,8 +103,9 @@ object Implicits { } } - if (refs.isEmpty) refs - else refs filter (refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution + if (refs.isEmpty) Nil + else refs.filter(refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution + .map(Candidate(_, level)) } } @@ -114,8 +121,8 @@ object Implicits { buf.toList } - /** The implicit references that are eligible for expected type `tp` */ - lazy val eligible: List[TermRef] = + /** The candidates that are eligible for expected type `tp` */ + lazy val eligible: List[Candidate] = /*>|>*/ track("eligible in tpe") /*<|<*/ { /*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ { if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length) @@ -135,10 +142,21 @@ object Implicits { * @param outerCtx the next outer context that makes visible further implicits */ class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) { - private val eligibleCache = new mutable.AnyRefMap[Type, List[TermRef]] + private val eligibleCache = new mutable.AnyRefMap[Type, List[Candidate]] + + /** The level increases if current context has a different owner or scope than + * the context of the next-outer ImplicitRefs. This is however disabled under + * Scala2 mode, since we do not want to change the implicit disambiguation then. + */ + override val level: Int = + if (outerImplicits == null) 1 + else if (ctx.scala2Mode || + (ctx.owner eq outerImplicits.ctx.owner) && + (ctx.scope eq outerImplicits.ctx.scope)) outerImplicits.level + else outerImplicits.level + 1 /** The implicit references that are eligible for type `tp`. */ - def eligible(tp: Type): List[TermRef] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { + def eligible(tp: Type): List[Candidate] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ { if (tp.hash == NotCached) computeEligible(tp) else eligibleCache get tp match { case Some(eligibles) => @@ -162,13 +180,13 @@ object Implicits { } } - private def computeEligible(tp: Type): List[TermRef] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { + private def computeEligible(tp: Type): List[Candidate] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ { if (monitored) record(s"check eligible refs in ctx", refs.length) val ownEligible = filterMatching(tp) if (outerImplicits == NoContext.implicits) ownEligible else ownEligible ::: { - val shadowed = (ownEligible map (_.name)).toSet - outerImplicits.eligible(tp) filterNot (ref => shadowed contains ref.name) + val shadowed = ownEligible.map(_.ref.name).toSet + outerImplicits.eligible(tp).filterNot(cand => shadowed.contains(cand.ref.name)) } } @@ -198,8 +216,8 @@ object Implicits { * @param tree The typed tree that needs to be inserted * @param ctx The context after the implicit search */ - case class SearchSuccess(tree: tpd.Tree, ref: TermRef, tstate: TyperState) extends SearchResult { - override def toString = s"SearchSuccess($tree, $ref)" + case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult { + override def toString = s"SearchSuccess($tree, $ref, $level)" } /** A failed search */ @@ -478,7 +496,7 @@ trait Implicits { self: Typer => */ def inferImplicitArg(formal: Type, error: (String => String) => Unit, pos: Position)(implicit ctx: Context): Tree = inferImplicit(formal, EmptyTree, pos) match { - case SearchSuccess(arg, _, _) => + case SearchSuccess(arg, _, _, _) => arg case ambi: AmbiguousImplicits => error(where => s"ambiguous implicits: ${ambi.explanation} of $where") @@ -621,12 +639,13 @@ trait Implicits { self: Typer => protected def failedSearch: SearchFailure = NoImplicitMatches /** Search a list of eligible implicit references */ - def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = { + def searchImplicits(eligible: List[Candidate], contextual: Boolean): SearchResult = { val constr = ctx.typerState.constraint /** Try to typecheck an implicit reference */ - def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(i"typed implicit $ref, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { + def typedImplicit(cand: Candidate)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { assert(constr eq ctx.typerState.constraint) + val ref = cand.ref var generated: Tree = tpd.ref(ref).withPos(pos) if (!argument.isEmpty) generated = typedUnadapted( @@ -667,7 +686,7 @@ trait Implicits { self: Typer => if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) => nonMatchingImplicit(ref, Nil) case _ => - SearchSuccess(generated1, ref, ctx.typerState) + SearchSuccess(generated1, ref, cand.level, ctx.typerState) } }} @@ -676,19 +695,20 @@ trait Implicits { self: Typer => * @param pending The list of implicit references that remain to be investigated * @param acc An accumulator of successful matches found so far. */ - def rankImplicits(pending: List[TermRef], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { - case ref :: pending1 => + def rankImplicits(pending: List[Candidate], acc: List[SearchSuccess]): List[SearchSuccess] = pending match { + case cand :: pending1 => val history = ctx.searchHistory nest wildProto val result = - if (history eq ctx.searchHistory) divergingImplicit(ref) - else typedImplicit(ref)(nestedContext.setNewTyperState.setSearchHistory(history)) + if (history eq ctx.searchHistory) divergingImplicit(cand.ref) + else typedImplicit(cand)(nestedContext.setNewTyperState.setSearchHistory(history)) result match { case fail: SearchFailure => rankImplicits(pending1, acc) case best: SearchSuccess => if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil else { - val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) + val newPending = pending1.filter(cand1 => + isAsGood(cand1.ref, best.ref, cand1.level, best.level)(nestedContext.setExploreTyperState)) rankImplicits(newPending, best :: acc) } } @@ -717,8 +737,9 @@ trait Implicits { self: Typer => /** Convert a (possibly empty) list of search successes into a single search result */ def condense(hits: List[SearchSuccess]): SearchResult = hits match { case best :: alts => - alts find (alt => isAsGood(alt.ref, best.ref)(ctx.fresh.setExploreTyperState)) match { + alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match { case Some(alt) => + typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}") /* !!! DEBUG println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}") isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState) @@ -735,16 +756,18 @@ trait Implicits { self: Typer => failedSearch } + def ranking(cand: Candidate) = -ctx.runInfo.useCount(cand.ref) + /** Sort list of implicit references according to their popularity * (# of times each was picked in current run). */ - def sort(eligible: List[TermRef]) = eligible match { + def sort(eligible: List[Candidate]) = eligible match { case Nil => eligible case e1 :: Nil => eligible case e1 :: e2 :: Nil => - if (ctx.runInfo.useCount(e1) < ctx.runInfo.useCount(e2)) e2 :: e1 :: Nil + if (ranking(e2) < ranking(e1)) e2 :: e1 :: Nil else eligible - case _ => eligible.sortBy(-ctx.runInfo.useCount(_)) + case _ => eligible.sortBy(ranking) } condense(rankImplicits(sort(eligible), Nil)) diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index b4ec3390e..e44343e70 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -98,7 +98,7 @@ class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImp * * TODO: Once we have fully bootstrapped, I would prefer if we expressed * unimport with an `override` modifier, and generalized it to all imports. - * I believe this would be more transparent than the curren set of conditions. E.g. + * I believe this would be more transparent than the current set of conditions. E.g. * * override import Predef.{any2stringAdd => _, StringAdd => _, _} // disables String + * override import java.lang.{} // disables all imports diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 1cb86dd72..86649d78e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -119,6 +119,18 @@ object Inferencing { } } + /** If `tree` has a PolyType, infer its type parameters by comparing with expected type `pt` */ + def inferTypeParams(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree.tpe match { + case poly: PolyType => + val (poly1, tvars) = constrained(poly, tree) + val tree1 = tree.withType(poly1).appliedToTypeTrees(tvars) + tree1.tpe <:< pt + fullyDefinedType(tree1.tpe, "template parent", tree.pos) + tree1 + case _ => + tree + } + /** The list of uninstantiated type variables bound by some prefix of type `T` which * occur in at least one formal parameter type of a prefix application. * Considered prefixes are: diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 3931fcaf4..09487570d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -189,7 +189,7 @@ object Inliner { if (!ctx.isAfterTyper) { val inlineCtx = ctx sym.updateAnnotation(LazyBodyAnnotation { _ => - implicit val ctx: Context = inlineCtx + implicit val ctx = inlineCtx ctx.withNoError(treeExpr(ctx))(makeInlineable) }) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index b8fe46745..1b6e437b5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -683,7 +683,7 @@ class Namer { typer: Typer => //println(i"completing type params of $sym in ${sym.owner}") nestedCtx = localContext(sym).setNewScope myTypeParams = { - implicit val ctx: Context = nestedCtx + implicit val ctx = nestedCtx val tparams = original.rhs match { case PolyTypeTree(tparams, _) => tparams case _ => Nil @@ -852,23 +852,23 @@ class Namer { typer: Typer => } } - /** Typecheck tree during completion, and remember result in typedtree map */ - private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { + /** Typecheck `tree` during completion using `typed`, and remember result in TypedAhead map */ + def typedAheadImpl(tree: Tree, typed: untpd.Tree => tpd.Tree)(implicit ctx: Context): tpd.Tree = { val xtree = expanded(tree) xtree.getAttachment(TypedAhead) match { case Some(ttree) => ttree case none => - val ttree = typer.typed(tree, pt) + val ttree = typed(tree) xtree.putAttachment(TypedAhead, ttree) ttree } } def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = - typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type) + typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type)) def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = - typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType) + typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrType)) def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match { case Apply(fn, _) => typedAheadAnnotation(fn) @@ -998,11 +998,16 @@ class Namer { typer: Typer => lhsType // keep constant types that fill in for a non-constant (to be revised when inline has landed). else inherited else { - if (sym is Implicit) { - val resStr = if (mdef.isInstanceOf[DefDef]) "result " else "" - ctx.error(s"${resStr}type of implicit definition needs to be given explicitly", mdef.pos) + def missingType(modifier: String) = { + ctx.error(s"${modifier}type of implicit definition needs to be given explicitly", mdef.pos) sym.resetFlag(Implicit) } + if (sym is Implicit) + mdef match { + case _: DefDef => missingType("result") + case _: ValDef if sym.owner.isType => missingType("") + case _ => + } lhsType orElse WildcardType } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 46bdbf3b3..dcbd444f9 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -18,7 +18,6 @@ import config.{ScalaVersion, NoScalaVersion} import Decorators._ import typer.ErrorReporting._ import DenotTransformers._ -import ValueClasses.isDerivedValueClass object RefChecks { import tpd._ @@ -688,39 +687,6 @@ object RefChecks { } } - /** Verify classes extending AnyVal meet the requirements */ - private def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = { - def checkValueClassMember(stat: Tree) = stat match { - case _: ValDef if !stat.symbol.is(ParamAccessor) => - ctx.error(s"value class may not define non-parameter field", stat.pos) - case _: DefDef if stat.symbol.isConstructor => - ctx.error(s"value class may not define secondary constructor", stat.pos) - case _: MemberDef | _: Import | EmptyTree => - // ok - case _ => - ctx.error(s"value class may not contain initialization statements", stat.pos) - } - if (isDerivedValueClass(clazz)) { - if (clazz.is(Trait)) - ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos) - if (clazz.is(Abstract)) - ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos) - if (!clazz.isStatic) - ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos) - else { - val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method)) - clParamAccessors match { - case List(param) => - if (param.is(Mutable)) - ctx.error("value class parameter must not be a var", param.pos) - case _ => - ctx.error("value class needs to have exactly one val parameter", clazz.pos) - } - } - stats.foreach(checkValueClassMember) - } - } - type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)] class OptLevelInfo extends DotClass { @@ -836,7 +802,6 @@ class RefChecks extends MiniPhase { thisTransformer => checkParents(cls) checkCompanionNameClashes(cls) checkAllOverrides(cls) - checkDerivedValueClass(cls, tree.body) tree } catch { case ex: MergeError => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ee2d68278..4a97648f5 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -189,9 +189,8 @@ trait TypeAssigner { val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) - if (!tpe.isError) - ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) - ErrorType + if (tpe.isError) tpe + else errorType(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) } } else if (d.symbol is TypeParamAccessor) @@ -215,17 +214,17 @@ trait TypeAssigner { else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) { TryDynamicCallType } else { - if (!site.isErroneous) { + if (site.isErroneous) UnspecifiedErrorType + else { def kind = if (name.isTypeName) "type" else "value" def addendum = if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?" else "" - ctx.error( + errorType( if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor" else NotAMember(site, name, kind), pos) } - ErrorType } } @@ -314,7 +313,8 @@ trait TypeAssigner { val ownType = fn.tpe.widen match { case fntpe @ MethodType(_, ptypes) => if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) - else wrongNumberOfArgs(fn.tpe, "", fntpe.typeParams, args, tree.pos) + else + errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${ptypes.length}, found: ${args.length}", tree.pos) case t => errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) } @@ -369,7 +369,7 @@ trait TypeAssigner { else { val argTypes = args.tpes if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) - else wrongNumberOfArgs(fn.tpe, "type", pt.typeParams, args, tree.pos) + else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos) } case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) @@ -462,7 +462,7 @@ trait TypeAssigner { val ownType = if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed) else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) - else wrongNumberOfArgs(tycon.tpe, "type", tparams, args, tree.pos) + else wrongNumberOfTypeArgs(tycon.tpe, tparams, args, tree.pos) tree.withType(ownType) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ae6b719e4..07a27a498 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -351,10 +351,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val ownType = if (rawType.exists) ensureAccessible(rawType, superAccess = false, tree.pos) - else { - error(new MissingIdent(tree, kind, name.show), tree.pos) - ErrorType - } + else + errorType(new MissingIdent(tree, kind, name.show), tree.pos) val tree1 = ownType match { case ownType: NamedType if !prefixIsElidable(ownType) => @@ -454,6 +452,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tree.tpt match { case templ: untpd.Template => import untpd._ + templ.parents foreach { + case parent: RefTree => + typedAheadImpl(parent, tree => inferTypeParams(typedType(tree), pt)) + case _ => + } val x = tpnme.ANON_CLASS val clsDef = TypeDef(x, templ).withFlags(Final) typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) @@ -519,7 +522,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper => inferImplicit(defn.ClassTagType.appliedTo(tref), EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match { - case SearchSuccess(arg, _, _) => + case SearchSuccess(arg, _, _, _) => return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt) case _ => } @@ -660,9 +663,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree - if (ctx.mode is Mode.Type) + if (ctx.mode is Mode.Type) { + val funCls = + if (tree.isInstanceOf[untpd.ImplicitFunction]) defn.ImplicitFunctionClass(args.length) + else defn.FunctionClass(args.length) typed(cpy.AppliedTypeTree(tree)( - untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) + untpd.TypeTree(funCls.typeRef), args :+ body), pt) + } else { val params = args.asInstanceOf[List[untpd.ValDef]] @@ -1052,7 +1059,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (hasNamedArg(args)) typedNamedArgs(args) else { if (args.length != tparams.length) { - wrongNumberOfArgs(tpt1.tpe, "type", tparams, args, tree.pos) + wrongNumberOfTypeArgs(tpt1.tpe, tparams, args, tree.pos) args = args.take(tparams.length) } def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { @@ -1293,6 +1300,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, cls, isRequired, cdef.pos) } + + // check value class constraints + checkDerivedValueClass(cls, body1) + cdef1 // todo later: check that @@ -1486,6 +1497,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.If => typedIf(tree, pt) case tree: untpd.Function => typedFunction(tree, pt) case tree: untpd.Closure => typedClosure(tree, pt) + case tree: untpd.Import => typedImport(tree, retrieveSym(tree)) case tree: untpd.Match => typedMatch(tree, pt) case tree: untpd.Return => typedReturn(tree) case tree: untpd.Try => typedTry(tree, pt) @@ -1513,9 +1525,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => typedUnadapted(desugar(tree), pt) } - xtree match { + if (defn.isImplicitFunctionType(pt) && + xtree.isTerm && + !untpd.isImplicitClosure(xtree) && + !ctx.isAfterTyper) + makeImplicitFunction(xtree, pt) + else xtree match { case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) - case xtree: untpd.Import => typedImport(xtree, retrieveSym(xtree)) case xtree => typedUnnamed(xtree) } } @@ -1524,6 +1540,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = untpd.rename(tree, tree.name.encode) + protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = { + val defn.FunctionOf(formals, resType, true) = pt.dealias + val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos)) + val ifun = desugar.makeImplicitFunction(paramTypes, tree) + typr.println(i"make implicit function $tree / $pt ---> $ifun") + typedUnadapted(ifun) + } + def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { assertPositioned(tree) try adapt(typedUnadapted(tree, pt), pt, tree) @@ -1608,6 +1632,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } + /** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */ + def isApplyProto(pt: Type)(implicit ctx: Context): Boolean = pt match { + case pt: SelectionProto => pt.name == nme.apply + case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType) + case pt: IgnoredProto => isApplyProto(pt.ignored) + case _ => false + } + /** Add apply node or implicit conversions. Two strategies are tried, and the first * that is successful is picked. If neither of the strategies are successful, continues with * `fallBack`. @@ -1621,14 +1653,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: => Tree)(implicit ctx: Context): Tree = { - /** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */ - def isApplyProto(pt: Type): Boolean = pt match { - case pt: SelectionProto => pt.name == nme.apply - case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType) - case pt: IgnoredProto => isApplyProto(pt.ignored) - case _ => false - } - def tryApply(implicit ctx: Context) = { val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) if (sel.tpe.isError) sel else adapt(sel, pt) @@ -1638,7 +1662,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack) if (isApplyProto(pt)) tryImplicit - else tryEither(tryApply(_))((_, _) => tryImplicit) + else tryEither(tryApply(_))((_, _) => tryImplicit) } /** If this tree is a select node `qual.name`, try to insert an implicit conversion @@ -1872,7 +1896,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit missingArgs case _ => ctx.typeComparer.GADTused = false - if (ctx.mode is Mode.Pattern) { + if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) && + !untpd.isImplicitClosure(tree) && + !isApplyProto(pt) && + !ctx.isAfterTyper) { + typr.println("insert apply on implicit $tree") + typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt) + } + else if (ctx.mode is Mode.Pattern) { tree match { case _: RefTree | _: Literal if !isVarPattern(tree) && @@ -1945,7 +1976,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } // try an implicit conversion inferView(tree, pt) match { - case SearchSuccess(inferred, _, _) => + case SearchSuccess(inferred, _, _, _) => adapt(inferred, pt) case failure: SearchFailure => if (pt.isInstanceOf[ProtoType] && !failure.isInstanceOf[AmbiguousImplicits]) tree @@ -1961,10 +1992,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else err.typeMismatch(tree1, pt) } + /** If tree has an error type but no errors are reported yet, issue + * the error message stored in the type. + * One way this can happen is if implicit search causes symbols and types + * to be completed. The types are stored by `typedAhead` so that they can be + * retrieved later and thus avoid duplication of typechecking work. + * But if the implicit search causing the `typedAhead` fails locally but + * another alternative succeeds we can be left with an ErrorType in the + * tree that went unreported. A scenario where this happens is i1802.scala. + */ + def ensureReported(tp: Type) = tp match { + case err: ErrorType if !ctx.reporter.hasErrors => ctx.error(err.msg, tree.pos) + case _ => + } + tree match { case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree case _ => tree.tpe.widen match { - case _: ErrorType => + case tp: FlexType => + ensureReported(tp) tree case ref: TermRef => pt match { diff --git a/compiler/test/dotc/scala-collections.whitelist b/compiler/test/dotc/scala-collections.whitelist index bb62b260a..e984af6c6 100644 --- a/compiler/test/dotc/scala-collections.whitelist +++ b/compiler/test/dotc/scala-collections.whitelist @@ -280,3 +280,5 @@ ../scala-scala/src/library/scala/collection/generic/Subtractable.scala ../scala-scala/src/library/scala/collection/generic/TraversableFactory.scala ../scala-scala/src/library/scala/collection/generic/package.scala + +../scala-scala/src/library/scala/util/Try.scala
\ No newline at end of file diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index 827e1addd..f858926d5 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -84,6 +84,7 @@ class tests extends CompilerTest { val runDir = testsDir + "run/" val newDir = testsDir + "new/" val replDir = testsDir + "repl/" + val javaDir = testsDir + "pos-java-interop/" val sourceDir = "./src/" val dottyDir = sourceDir + "dotty/" @@ -260,7 +261,6 @@ class tests extends CompilerTest { dotcDir + "config/PathResolver.scala" ), List(/* "-Ylog:frontend", */ "-Xprompt") ++ staleSymbolError ++ twice) - val javaDir = "./tests/pos-java-interop/" @Test def java_all = compileFiles(javaDir, twice) //@Test def dotc_compilercommand = compileFile(dotcDir + "config/", "CompilerCommand") @@ -349,9 +349,10 @@ class tests extends CompilerTest { @Test def tasty_tests = compileDir(testsDir, "tasty", testPickling) @Test def tasty_bootstrap = { - val opt = List("-priorityclasspath", defaultOutputDir, "-Ylog-classpath") + val logging = if (false) List("-Ylog-classpath", "-verbose") else Nil + val opt = List("-priorityclasspath", defaultOutputDir) ++ logging // first compile dotty - compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant", "-strict"))(allowDeepSubtypes) + compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant", "-strict") ++ logging)(allowDeepSubtypes) compileDir(libDir, "dotty", "-deep" :: opt) compileDir(libDir, "scala", "-deep" :: opt) diff --git a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala index e31ef2160..32f842e92 100644 --- a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala @@ -140,12 +140,12 @@ class ModifiersParsingTest { source = "def f(implicit a: Int, b: Int) = ???" println(source.defParam(0).modifiers) - assert(source.defParam(0).modifiers == List(Mod.Implicit(Flags.Implicit))) - assert(source.defParam(1).modifiers == List(Mod.Implicit(Flags.Implicit))) + assert(source.defParam(0).modifiers == List(Mod.Implicit())) + assert(source.defParam(1).modifiers == List(Mod.Implicit())) source = "def f(x: Int, y: Int)(implicit a: Int, b: Int) = ???" assert(source.defParam(0, 0).modifiers == List()) - assert(source.defParam(1, 0).modifiers == List(Mod.Implicit(Flags.Implicit))) + assert(source.defParam(1, 0).modifiers == List(Mod.Implicit())) } @Test def blockDef = { |