diff options
Diffstat (limited to 'compiler')
16 files changed, 150 insertions, 53 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 29629e505..6b7be12be 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -202,7 +202,7 @@ object Contexts { private[core] var pendingMemberSearches: List[Name] = Nil /** The new implicit references that are introduced by this scope */ - private var implicitsCache: ContextualImplicits = null + protected var implicitsCache: ContextualImplicits = null def implicits: ContextualImplicits = { if (implicitsCache == null ) implicitsCache = { @@ -469,6 +469,7 @@ object Contexts { def setTypeAssigner(typeAssigner: TypeAssigner): this.type = { this.typeAssigner = typeAssigner; this } def setTyper(typer: Typer): this.type = { this.scope = typer.scope; setTypeAssigner(typer) } def setImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this } + def setImplicits(implicits: ContextualImplicits): this.type = { this.implicitsCache = implicits; this } def setRunInfo(runInfo: RunInfo): this.type = { this.runInfo = runInfo; this } def setDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this } def setGadt(gadt: GADTMap): this.type = { this.gadt = gadt; this } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 847177e2f..79e97becb 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -906,13 +906,6 @@ class Definitions { /** The type of the boxed class corresponding to primitive value type `tp`. */ def boxedType(tp: Type)(implicit ctx: Context): TypeRef = boxedTypes(scalaClassName(tp)) - def wrapArrayMethodName(elemtp: Type): TermName = { - val cls = elemtp.classSymbol - if (cls.isPrimitiveValueClass) nme.wrapXArray(cls.name) - else if (cls.derivesFrom(ObjectClass) && !cls.isPhantomClass) nme.wrapRefArray - else nme.genericWrapArray - } - type PrimitiveClassEnc = Int val ByteEnc = 2 diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c424c6182..1e36361f8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -130,6 +130,7 @@ object StdNames { val COMPANION_CLASS_METHOD: N = "companion$class" val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" val DIRECT_SUFFIX: N = "$direct" + val LAZY_IMPLICIT_PREFIX: N = "$lazy_implicit$" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 5d0dd2123..c5e064478 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -258,6 +258,10 @@ trait Symbols { this: Context => def newDefaultConstructor(cls: ClassSymbol) = newConstructor(cls, EmptyFlags, Nil, Nil) + /** Create a synthetic lazy implicit value */ + def newLazyImplicit(info: Type) = + newSymbol(owner, freshName(nme.LAZY_IMPLICIT_PREFIX).toTermName, Lazy, info) + /** Create a symbol representing a selftype declaration for class `cls`. */ def newSelfSym(cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType): TermSymbol = ctx.newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c14108d2e..b27bff37a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1607,6 +1607,7 @@ object Parsers { * LocalModifier ::= abstract | final | sealed | implicit | lazy */ def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = { + @tailrec def loop(mods: Modifiers): Modifiers = { if (allowed contains in.token) { val isAccessMod = accessModifierTokens contains in.token @@ -2057,7 +2058,7 @@ object Parsers { val name = ident().toTypeName val constr = atPos(in.lastOffset) { val tparams = typeParamClauseOpt(ParamOwner.Class) - val cmods = constrModsOpt() + val cmods = constrModsOpt(name) val vparamss = paramClauses(name, mods is Case) makeConstructor(tparams, vparamss).withMods(cmods) @@ -2070,11 +2071,11 @@ object Parsers { /** ConstrMods ::= AccessModifier * | Annotation {Annotation} (AccessModifier | `this') */ - def constrModsOpt(): Modifiers = { + def constrModsOpt(owner: Name): Modifiers = { val mods = modifiers(accessModifierTokens, annotsAsMods()) if (mods.hasAnnotations && !mods.hasFlags) if (in.token == THIS) in.nextToken() - else syntaxError("`private', `protected', or `this' expected") + else syntaxError(AnnotatedPrimaryConstructorRequiresModifierOrThis(owner), mods.annotations.last.pos) mods } diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index 86f34e64d..e20f846ac 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -6,6 +6,7 @@ import parsing.Tokens._ import scala.annotation.switch import scala.collection.mutable.StringBuilder import core.Contexts.Context +import util.Chars.{ LF, FF, CR, SU } import Highlighting.{Highlight, HighlightBuffer} /** This object provides functions for syntax highlighting in the REPL */ @@ -26,7 +27,7 @@ object SyntaxHighlighting { private def valDef(str: String) = ValDefColor + str + NoColor private def operator(str: String) = TypeColor + str + NoColor private def annotation(str: String) = - if (str.trim == "@") str else AnnotationColor + str + NoColor + if (str.trim == "@") str else { AnnotationColor + str + NoColor } private val tripleQs = Console.RED_B + "???" + NoColor private val keywords: Seq[String] = for { @@ -152,7 +153,11 @@ object SyntaxHighlighting { var open = 1 while (open > 0 && remaining.nonEmpty) { curr = takeChar() - newBuf += curr + if (curr == '@') { + appendWhile('@', !typeEnders.contains(_), annotation) + newBuf append CommentColor + } + else newBuf += curr if (curr == '*' && remaining.nonEmpty) { curr = takeChar() @@ -163,6 +168,11 @@ object SyntaxHighlighting { newBuf += curr if (curr == '*') open += 1 } + + (curr: @switch) match { + case LF | FF | CR | SU => newBuf append CommentColor + case _ => () + } } prev = curr newBuf append NoColor @@ -236,6 +246,11 @@ object SyntaxHighlighting { newBuf += curr closing = 0 } + + (curr: @switch) match { + case LF | FF | CR | SU => newBuf append LiteralColor + case _ => () + } } newBuf append NoColor prev = curr diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 190445d60..17eb8d39b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -5,9 +5,12 @@ package reporting import core.Contexts.Context import core.Decorators._ import printing.Highlighting.{Blue, Red} +import printing.SyntaxHighlighting import diagnostic.{ErrorMessageID, Message, MessageContainer, NoExplanation} import diagnostic.messages._ import util.SourcePosition +import util.Chars.{ LF, CR, FF, SU } +import scala.annotation.switch import scala.collection.mutable @@ -38,20 +41,37 @@ trait MessageRendering { */ def sourceLines(pos: SourcePosition)(implicit ctx: Context): (List[String], List[String], Int) = { var maxLen = Int.MinValue - def render(xs: List[Int]) = - xs.map(pos.source.offsetToLine(_)) - .map { lineNbr => - val prefix = s"${lineNbr + 1} |" - maxLen = math.max(maxLen, prefix.length) - (prefix, pos.lineContent(lineNbr).stripLineEnd) - } - .map { case (prefix, line) => - val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix) - hl"$lnum$line" - } + def render(offsetAndLine: (Int, String)): String = { + val (offset, line) = offsetAndLine + val lineNbr = pos.source.offsetToLine(offset) + val prefix = s"${lineNbr + 1} |" + maxLen = math.max(maxLen, prefix.length) + val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix).show + lnum + line.stripLineEnd + } + + def linesFrom(arr: Array[Char]): List[String] = { + def pred(c: Char) = (c: @switch) match { + case LF | CR | FF | SU => true + case _ => false + } + val (line, rest0) = arr.span(!pred(_)) + val (_, rest) = rest0.span(pred) + new String(line) :: { if (rest.isEmpty) Nil else linesFrom(rest) } + } + val syntax = + if (ctx.settings.color.value != "never") + SyntaxHighlighting(pos.linesSlice).toArray + else pos.linesSlice + val lines = linesFrom(syntax) val (before, after) = pos.beforeAndAfterPoint - (render(before), render(after), maxLen) + + ( + before.zip(lines).map(render), + after.zip(lines.drop(before.length)).map(render), + maxLen + ) } /** The column markers aligned under the error */ diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 75e1d4cb2..02869e752 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -51,6 +51,7 @@ public enum ErrorMessageID { ExpectedTokenButFoundID, MixedLeftAndRightAssociativeOpsID, CantInstantiateAbstractClassOrTraitID, + AnnotatedPrimaryConstructorRequiresModifierOrThisID, OverloadedOrRecursiveMethodNeedsResultTypeID, RecursiveValueNeedsResultTypeID, CyclicReferenceInvolvingImplicitID, diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 8d34d97ed..f2f69b6cc 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1151,7 +1151,9 @@ object messages { val kind = "Syntax" val msg = hl"""overloaded or recursive method ${tree} needs result type""" val explanation = - hl"""""".stripMargin + hl""" + | + """.stripMargin } case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context) @@ -1172,6 +1174,18 @@ object messages { |""".stripMargin } - - + case class AnnotatedPrimaryConstructorRequiresModifierOrThis(cls: Name)(implicit ctx: Context) + extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) { + val kind = "Syntax" + val msg = hl"""${"private"}, ${"protected"}, or ${"this"} expected for annotated primary constructor""" + val explanation = + hl"""|When using annotations with a primary constructor of a class, + |the annotation must be followed by an access modifier + |(${"private"} or ${"protected"}) or ${"this"}. + | + |For example: + | ${"class Sample @deprecated this(param: Parameter) { ..."} + | ^^^^ + |""".stripMargin + } } diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index 25aa9ffaf..9f7ceeaed 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -40,6 +40,12 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: def isCurrent(sym: Symbol) = ctx.atPhase(thisTransform) { implicit ctx => cls.info.member(sym.name).hasAltWith(_.symbol == sym) + // this is a hot spot, where we spend several seconds while compiling stdlib + // unfortunately it will discard and recompute all the member chaches, + // both making itself slow and slowing down anything that runs after it + // because resolveSuper uses hacks with explicit adding to scopes through .enter + // this cannot be fixed by a smarter caching strategy. With current implementation + // we HAVE to discard caches here for correctness } /** Does `method` need a forwarder to in class `cls` @@ -53,8 +59,8 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: def needsDisambiguation = competingMethods.exists(x=> !(x is Deferred)) // multiple implementations are available def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - isCurrent(meth) && - (needsDisambiguation || hasNonInterfaceDefinition || meth.owner.is(Scala2x)) + (meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition ) && + isCurrent(meth) } /** Get `sym` of the method that needs a forwarder diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index de017961a..e9df12b42 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -450,7 +450,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def typedArg(arg: Arg, formal: Type): Arg = arg def addArg(arg: TypedArg, formal: Type) = - ok = ok & isCompatible(argType(arg, formal), formal) + ok = ok & { + argType(arg, formal) match { + case ref: TermRef if ref.denot.isOverloaded => + // in this case we could not resolve overloading because no alternative + // matches expected type + false + case argtpe => + isCompatible(argtpe, formal) + } + } def makeVarArg(n: Int, elemFormal: Type) = {} def fail(msg: => Message, arg: Arg) = ok = false diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 48cf0cfac..d5afae90c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -29,6 +29,7 @@ import reporting.diagnostic.MessageContainer import Inferencing.fullyDefinedType import Trees._ import Hashable._ +import util.Property import config.Config import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable @@ -36,6 +37,11 @@ import collection.mutable /** Implicit resolution */ object Implicits { + /** A reference to an implicit value to be made visible on the next nested call to + * inferImplicitArg with a by-name expected type. + */ + val DelayedImplicit = new Property.Key[TermRef] + /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ case class Candidate(ref: TermRef, level: Int) @@ -202,7 +208,7 @@ object Implicits { } override def toString = { - val own = s"(implicits: ${refs mkString ","})" + val own = i"(implicits: $refs%, %)" if (isOuterMost) own else own + "\n " + outerImplicits } @@ -537,27 +543,56 @@ trait Implicits { self: Typer => else EmptyTree } - inferImplicit(formal, EmptyTree, pos) match { + /** The context to be used when resolving a by-name implicit argument. + * This makes any implicit stored under `DelayedImplicit` visible and + * stores in turn the given `lazyImplicit` as new `DelayedImplicit`. + */ + def lazyImplicitCtx(lazyImplicit: Symbol): Context = { + val lctx = ctx.fresh + for (delayedRef <- ctx.property(DelayedImplicit)) + lctx.setImplicits(new ContextualImplicits(delayedRef :: Nil, ctx.implicits)(ctx)) + lctx.setProperty(DelayedImplicit, lazyImplicit.termRef) + } + + /** formalValue: The value type for which an implicit is searched + * lazyImplicit: An implicit symbol to install for nested by-name resolutions + * argCtx : The context to be used for searching the implicit argument + */ + val (formalValue, lazyImplicit, argCtx) = formal match { + case ExprType(fv) => + val lazyImplicit = ctx.newLazyImplicit(fv) + (fv, lazyImplicit, lazyImplicitCtx(lazyImplicit)) + case _ => (formal, NoSymbol, ctx) + } + + inferImplicit(formalValue, EmptyTree, pos)(argCtx) match { case SearchSuccess(arg, _, _, _) => - arg + def refersToLazyImplicit = arg.existsSubTree { + case id: Ident => id.symbol == lazyImplicit + case _ => false + } + if (lazyImplicit.exists && refersToLazyImplicit) + Block(ValDef(lazyImplicit.asTerm, arg).withPos(pos) :: Nil, ref(lazyImplicit)) + else + arg case ambi: AmbiguousImplicits => error(where => s"ambiguous implicits: ${ambi.explanation} of $where") EmptyTree case failure: SearchFailure => - val arg = synthesizedClassTag(formal, pos) + val arg = synthesizedClassTag(formalValue, pos) if (!arg.isEmpty) arg else { var msgFn = (where: String) => em"no implicit argument of type $formal found for $where" + failure.postscript for { - notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot) + notFound <- formalValue.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot) Trees.Literal(Constant(raw: String)) <- notFound.argument(0) } { msgFn = where => err.implicitNotFoundString( raw, - formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString), - formal.argInfos) + formalValue.typeSymbol.typeParams.map(_.name.unexpandedName.toString), + formalValue.argInfos) } error(msgFn) EmptyTree diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 06200d3e4..33e320ce5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1890,7 +1890,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def implicitArgError(msg: String => String) = errors += (() => msg(em"parameter $pname of $methodStr")) if (errors.nonEmpty) EmptyTree - else inferImplicitArg(formal.widenExpr, implicitArgError, tree.pos.endPos) + else inferImplicitArg(formal, implicitArgError, tree.pos.endPos) } if (errors.nonEmpty) { // If there are several arguments, some arguments might already diff --git a/compiler/src/dotty/tools/dotc/util/SourcePosition.scala b/compiler/src/dotty/tools/dotc/util/SourcePosition.scala index aad4995d8..be5c46995 100644 --- a/compiler/src/dotty/tools/dotc/util/SourcePosition.scala +++ b/compiler/src/dotty/tools/dotc/util/SourcePosition.scala @@ -12,9 +12,14 @@ extends interfaces.SourcePosition { def lineContent: String = source.lineContent(point) def point: Int = pos.point + /** The line of the position, starting at 0 */ def line: Int = source.offsetToLine(point) + /** Extracts the lines from the underlying source file as `Array[Char]`*/ + def linesSlice: Array[Char] = + source.content.slice(source.startOfLine(start), source.nextLine(end)) + /** The lines of the position */ def lines: List[Int] = List.range(source.offsetToLine(start), source.offsetToLine(end + 1)) match { @@ -25,9 +30,6 @@ extends interfaces.SourcePosition { def lineOffsets: List[Int] = lines.map(source.lineToOffset(_)) - def lineContent(lineNumber: Int): String = - source.lineContent(source.lineToOffset(lineNumber)) - def beforeAndAfterPoint: (List[Int], List[Int]) = lineOffsets.partition(_ <= point) diff --git a/compiler/test/dotty/tools/dotc/EntryPointsTest.scala.disabled b/compiler/test/dotty/tools/dotc/EntryPointsTest.scala.disabled index 00918a282..98e624818 100644 --- a/compiler/test/dotty/tools/dotc/EntryPointsTest.scala.disabled +++ b/compiler/test/dotty/tools/dotc/EntryPointsTest.scala.disabled @@ -4,7 +4,7 @@ package dotc import org.junit.Test import org.junit.Assert._ -import dotty.tools.dotc.interfaces.{CompilerCallback, SourceFile} +import interfaces.{CompilerCallback, SourceFile} import reporting._ import reporting.diagnostic.MessageContainer import core.Contexts._ diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 71d62405a..d6e687a1e 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -198,13 +198,10 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertTrue("expected trait", isTrait) } - @Test def cantInstantiateTrait = - checkMessagesAfter("refchecks") { + @Test def constructorModifier = + checkMessagesAfter("frontend") { """ - |object Scope { - | trait Concept - | new Concept() - |} + |class AnotherClass @deprecated () """.stripMargin } .expect { (ictx, messages) => @@ -212,9 +209,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { val defn = ictx.definitions assertMessageCount(1, messages) - val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages - assertEquals("Concept", cls.name.show) - assertTrue("expected trait", isTrait) + val AnnotatedPrimaryConstructorRequiresModifierOrThis(cls) :: Nil = messages + assertEquals("AnotherClass", cls.show) } - } |