diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
9 files changed, 143 insertions, 55 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index fcfcc8feb9..0910dca445 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -655,9 +655,6 @@ trait ContextErrors { def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) = NormalTypeError(parent, "illegal inheritance from final "+mixin) - def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = - NormalTypeError(parent, "illegal inheritance from sealed " + psym ) - def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) = NormalTypeError(parent, "illegal inheritance;\n self-type "+selfType+" does not conform to "+ @@ -1172,6 +1169,9 @@ trait ContextErrors { def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") + def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = + NormalTypeError(parent, "illegal inheritance from sealed " + psym ) + def RootImportError(tree: Tree) = issueNormalTypeError(tree, "_root_ cannot be imported") @@ -1211,7 +1211,7 @@ trait ContextErrors { "pass-by-name arguments not allowed for case class parameters" case AbstractVar => - "only classes can have declared but undefined members" + abstractVarMessage(sym) + "only traits and abstract classes can have declared but undefined members" + abstractVarMessage(sym) } issueSymbolTypeError(sym, msg) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c73ea54c3d..d349597b14 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -60,7 +60,7 @@ trait Contexts { self: Analyzer => private lazy val allImportInfos = mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil - def warnUnusedImports(unit: CompilationUnit) = { + def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) { for (imps <- allImportInfos.remove(unit)) { for (imp <- imps.reverse.distinct) { val used = allUsedSelectors(imp) @@ -1192,6 +1192,28 @@ trait Contexts { self: Analyzer => } res } + + final def lookupCompanionOf(original: Symbol): Symbol = { + if (original.isModuleClass) original.sourceModule + else lookupScopeEntry(original) match { + case null => NoSymbol + case entry => entry.owner.lookupCompanion(original) + } + } + + /** Search scopes in current and enclosing contexts for the definition of `symbol` */ + private def lookupScopeEntry(symbol: Symbol): ScopeEntry = { + var res: ScopeEntry = null + var ctx = this + while (res == null && ctx.outer != ctx) { + val s = ctx.scope lookupSymbolEntry symbol + if (s != null) + res = s + else + ctx = ctx.outer + } + res + } } //class Context /** A `Context` focussed on an `Import` tree */ diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 97de2b6c85..5f4fa499b6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -105,7 +105,7 @@ trait EtaExpansion { self: Analyzer => val origTpe = sym.tpe val isRepeated = definitions.isRepeatedParamType(origTpe) // SI-4176 Don't leak A* in eta-expanded function types. See t4176b.scala - val droppedStarTpe = if (settings.etaExpandKeepsStar) origTpe else dropIllegalStarTypes(origTpe) + val droppedStarTpe = dropIllegalStarTypes(origTpe) val valDef = ValDef(Modifiers(SYNTHETIC | PARAM), sym.name.toTermName, TypeTree(droppedStarTpe), EmptyTree) (valDef, isRepeated) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 0071d66eb9..e8147dbf3a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -936,10 +936,8 @@ trait Infer extends Checkable { def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString "," printTyping(tree, s"infer expr instance from pt=$pt, $infer_s") - // SI-7899 inferring by-name types is unsound. The correct behaviour is conditional because the hole is - // exploited in Scalaz (Free.scala), as seen in: run/t7899-regression. - def dropByNameIfStrict(tp: Type): Type = if (settings.inferByName) tp else dropByName(tp) - def targsStrict = if (targs eq null) null else targs mapConserve dropByNameIfStrict + // SI-7899 inferring by-name types is unsound + def targsStrict = if (targs eq null) null else targs mapConserve dropByName if (keepNothings || (targs eq null)) { //@M: adjustTypeArgs fails if targs==null, neg/t0226 substExpr(tree, tparams, targsStrict, pt) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index d11417192d..0f257d3717 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -132,7 +132,11 @@ trait MethodSynthesis { // only one symbol can have `tree.pos`, the others must focus their position // normally the field gets the range position, but if there is none, give it to the getter + // + // SI-10009 the tree's modifiers can be temporarily out of sync with the new symbol's flags. + // typedValDef corrects this later on. tree.symbol = fieldSym orElse (getterSym setPos tree.pos) + val namer = namerOf(tree.symbol) // the valdef gets the accessor symbol for a lazy val (too much going on in its RHS) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 78e8c8c073..395bda234b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -115,7 +115,7 @@ trait Namers extends MethodSynthesis { protected def owner = context.owner def contextFile = context.unit.source.file def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // H@ need to ensure that we handle only cyclic references TypeSigError(tree, ex) alt @@ -220,7 +220,10 @@ trait Namers extends MethodSynthesis { private def inCurrentScope(m: Symbol): Boolean = { if (owner.isClass) owner == m.owner - else m.owner.isClass && context.scope == m.owner.info.decls + else context.scope.lookupSymbolEntry(m) match { + case null => false + case entry => entry.owner eq context.scope + } } /** Enter symbol into context's scope and return symbol itself */ @@ -902,9 +905,10 @@ trait Namers extends MethodSynthesis { // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. // The defaults are: // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity. - // - val/var member annotations solely end up on the underlying field, except in traits (@since 2.12), + // - val/var member annotations solely end up on the underlying field, except in traits and for all lazy vals (@since 2.12), // where there is no field, and the getter thus holds annotations targeting both getter & field. - // As soon as there is a field/getter (in subclasses mixing in the trait), we triage the annotations. + // As soon as there is a field/getter (in subclasses mixing in the trait, or after expanding the lazy val during the fields phase), + // we triage the annotations. // // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) @@ -918,15 +922,17 @@ trait Namers extends MethodSynthesis { BeanPropertyAnnotationLimitationError(tree) } + val canTriageAnnotations = isSetter || !fields.getterTreeAnnotationsTargetFieldAndGetter(owner, mods) + def filterAccessorAnnotations: AnnotationInfo => Boolean = - if (isSetter || !owner.isTrait) + if (canTriageAnnotations) annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) else (ann => annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) def filterBeanAccessorAnnotations: AnnotationInfo => Boolean = - if (isSetter || !owner.isTrait) + if (canTriageAnnotations) annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) else (ann => annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || @@ -1028,12 +1034,33 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner + + val parentTrees = typer.typedParentTypes(templ) + + val pending = mutable.ListBuffer[AbsTypeError]() + parentTrees foreach { tpt => + val ptpe = tpt.tpe + if(!ptpe.isError) { + val psym = ptpe.typeSymbol + val sameSourceFile = context.unit.source.file == psym.sourceFile + + if (psym.isSealed && !phase.erasedTypes) + if (sameSourceFile) + psym addChild context.owner + else + pending += ParentSealedInheritanceError(tpt, psym) + if (psym.isLocalToBlock && !phase.erasedTypes) + psym addChild context.owner + } + } + pending.foreach(ErrorUtils.issueTypeError) + def checkParent(tpt: Tree): Type = { if (tpt.tpe.isError) AnyRefTpe else tpt.tpe } - val parents = typer.typedParentTypes(templ) map checkParent + val parents = parentTrees map checkParent enterSelf(templ.self) @@ -1827,6 +1854,12 @@ trait Namers extends MethodSynthesis { abstract class TypeCompleter extends LazyType { val tree: Tree + override def forceDirectSuperclasses: Unit = { + tree.foreach { + case dt: DefTree => global.withPropagateCyclicReferences(Option(dt.symbol).map(_.maybeInitialize)) + case _ => + } + } } def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter { @@ -1923,10 +1956,7 @@ trait Namers extends MethodSynthesis { // use the lower-level scan through the current Context as a fall back. if (!currentRun.compiles(owner)) owner.initialize original.companionSymbol orElse { - ctx.lookup(original.name.companionName, owner).suchThat(sym => - (original.isTerm || sym.hasModuleFlag) && - (sym isCoDefinedWith original) - ) + ctx.lookupCompanionOf(original) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 1df3449ce6..cd0c292d90 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -123,7 +123,7 @@ trait PatternTypers { } private def boundedArrayType(bound: Type): Type = { - val tparam = context.owner freshExistential "" setInfo (TypeBounds upper bound) + val tparam = context.owner.freshExistential("", 0) setInfo (TypeBounds upper bound) newExistentialType(tparam :: Nil, arrayType(tparam.tpe_*)) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 08cd5e5450..45dfb427f0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1158,6 +1158,7 @@ abstract class RefChecks extends Transform { } } checkUndesiredProperties(rtpe.typeSymbol, tree.pos) + checkUndesiredProperties(rtpe.typeSymbol.primaryConstructor, tree.pos) tree } @@ -1412,6 +1413,12 @@ abstract class RefChecks extends Transform { transformTrees(annots flatMap (_.args)) } + def checkIsElisible(sym: Symbol) = if (sym ne null) sym.elisionLevel.foreach { level => + if (!sym.isMethod || sym.isAccessor || sym.isLazy || sym.isDeferred) + reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable.") + } + if (settings.isScala213) checkIsElisible(tree.symbol) + tree match { case m: MemberDef => val sym = m.symbol @@ -1425,7 +1432,7 @@ abstract class RefChecks extends Transform { analyzer.ImplicitAmbiguousMsg.check(sym) foreach messageWarning("implicitAmbiguous") case tpt@TypeTree() => - if(tpt.original != null) { + if (tpt.original != null) { tpt.original foreach { case dc@TypeTreeWithDeferredRefCheck() => applyRefchecksToAnnotations(dc.check()) // #2416 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 192917d4aa..837ccf7e06 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,11 +13,12 @@ package scala package tools.nsc package typechecker -import scala.collection.{mutable, immutable} -import scala.reflect.internal.util.{ Statistics, ListOfNil } +import scala.collection.{immutable, mutable} +import scala.reflect.internal.util.{ListOfNil, Statistics} import mutable.ListBuffer import symtab.Flags._ import Mode._ +import scala.reflect.macros.whitebox // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -1690,7 +1691,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) } catch { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // fallback in case of cyclic errors // @H none of the tests enter here but I couldn't rule it out // upd. @E when a definition inherits itself, we end up here @@ -1751,13 +1752,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.deprecationWarning(parent.pos, psym, report, version) } - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner - else - pending += ParentSealedInheritanceError(parent, psym) - if (psym.isLocalToBlock && !phase.erasedTypes) - psym addChild context.owner val parentTypeOfThis = parent.tpe.dealias.typeOfThis if (!(selfType <:< parentTypeOfThis) && @@ -2033,7 +2027,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // use typedValDef instead. this version is called after creating a new context for the ValDef private def typedValDefImpl(vdef: ValDef) = { val sym = vdef.symbol.initialize - val typedMods = typedModifiers(vdef.mods) + val typedMods = if (nme.isLocalName(sym.name) && sym.isPrivateThis && !vdef.mods.isPrivateLocal) { + // SI-10009 This tree has been given a field symbol by `enterGetterSetter`, patch up the + // modifiers accordingly so that we can survive resetAttrs and retypechecking. + // Similarly, we use `sym.name` rather than `vdef.name` below to use the local name. + typedModifiers(vdef.mods.copy(flags = sym.flags, privateWithin = tpnme.EMPTY)) + } else typedModifiers(vdef.mods) sym.annotations.map(_.completeInfo()) val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) @@ -2041,7 +2040,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // allow trait accessors: it's the only vehicle we have to hang on to annotations that must be passed down to // the field that's mixed into a subclass - if (sym.hasAnnotation(definitions.VolatileAttr) && !((sym hasFlag MUTABLE) || (sym hasFlag ACCESSOR) && sym.owner.isTrait)) + if (sym.hasAnnotation(definitions.VolatileAttr) && !((sym hasFlag MUTABLE | LAZY) || (sym hasFlag ACCESSOR) && sym.owner.isTrait)) VolatileValueError(vdef) val rhs1 = @@ -2068,7 +2067,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else tpt1.tpe transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } - treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType + treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType } /** Enter all aliases of local parameter accessors. @@ -2568,7 +2567,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + if (!(settings.Yvirtpatmat && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match)), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize @@ -3415,7 +3414,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // governed by a) the argument types and b) the expected type val args1 = typedArgs(args, forArgMode(fun, mode)) val pts = args1.map(_.tpe.deconst) - val clone = fun.symbol.cloneSymbol + val clone = fun.symbol.cloneSymbol.withoutAnnotations val cloneParams = pts map (pt => clone.newValueParameter(currentUnit.freshTermName()).setInfo(pt)) val resultType = if (isFullyDefined(pt)) pt else ObjectTpe clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType)) @@ -4638,22 +4637,55 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val appStart = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null - def onError(reportError: => Tree): Tree = fun match { - case Select(qual, name) - if !mode.inPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) && !qual.exists(_.isErroneous) => + def isConversionCandidate(qual: Tree, name: Name): Boolean = + !mode.inPatternMode && nme.isOpAssignmentName(TermName(name.decode)) && !qual.exists(_.isErroneous) + def reportError(error: SilentTypeError): Tree = { + error.reportableErrors foreach context.issue + error.warnings foreach { case (p, m) => context.warning(p, m) } + args foreach (arg => typed(arg, mode, ErrorType)) + setError(tree) + } + def advice1(convo: Tree, errors: List[AbsTypeError], err: SilentTypeError): List[AbsTypeError] = + errors.map { e => + if (e.errPos == tree.pos) { + val header = f"${e.errMsg}%n Expression does not convert to assignment because:%n " + val expansion = f"%n expansion: ${show(convo)}" + NormalTypeError(tree, err.errors.flatMap(_.errMsg.lines.toList).mkString(header, f"%n ", expansion)) + } else e + } + def advice2(errors: List[AbsTypeError]): List[AbsTypeError] = + errors.map { e => + if (e.errPos == tree.pos) { + val msg = f"${e.errMsg}%n Expression does not convert to assignment because receiver is not assignable." + NormalTypeError(tree, msg) + } else e + } + def onError(error: SilentTypeError): Tree = fun match { + case Select(qual, name) if isConversionCandidate(qual, name) => val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) - convertToAssignment(fun, qual1, name, args) + val erred = qual1.isErroneous || args.exists(_.isErroneous) + if (erred) reportError(error) else { + val convo = convertToAssignment(fun, qual1, name, args) + silent(op = _.typed1(convo, mode, pt)) match { + case SilentResultValue(t) => t + case err: SilentTypeError => reportError(SilentTypeError(advice1(convo, error.errors, err), error.warnings)) + } + } } else { if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError + val Apply(Select(qual2, _), args2) = tree + val erred = qual2.isErroneous || args2.exists(_.isErroneous) + reportError { + if (erred) error else SilentTypeError(advice2(error.errors), error.warnings) + } } case _ => if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) - reportError + reportError(error) } val silentResult = silent( op = _.typed(fun, mode.forFunMode, funpt), @@ -4678,13 +4710,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tryTypedApply(fun2, args) else doTypedApply(tree, fun2, args, mode, pt) - case err: SilentTypeError => - onError({ - err.reportableErrors foreach context.issue - err.warnings foreach { case (p, m) => context.warning(p, m) } - args foreach (arg => typed(arg, mode, ErrorType)) - setError(tree) - }) + case err: SilentTypeError => onError(err) } } @@ -4727,7 +4753,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper Select(vble.duplicate, prefix) setPos fun.pos.focus, args) setPos tree.pos.makeTransparent ) setPos tree.pos - def mkUpdate(table: Tree, indices: List[Tree]) = { + def mkUpdate(table: Tree, indices: List[Tree]) = gen.evalOnceAll(table :: indices, context.owner, context.unit) { case tab :: is => def mkCall(name: Name, extraArgs: Tree*) = ( @@ -4742,9 +4768,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ) case _ => EmptyTree } - } - val tree1 = qual match { + val assignment = qual match { case Ident(_) => mkAssign(qual) @@ -4760,7 +4785,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => UnexpectedTreeAssignmentConversionError(qual) } } - typed1(tree1, mode, pt) + assignment } def typedSuper(tree: Super) = { @@ -5561,6 +5586,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } try runTyper() catch { + case ex: CyclicReference if global.propagateCyclicReferences => + throw ex case ex: TypeError => tree.clearType() // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. |