diff options
58 files changed, 1336 insertions, 196 deletions
diff --git a/.gitignore b/.gitignore index 9536d0ebc..7c5baf8f8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ project/boot/ project/plugins/project/ project/local-plugins.sbt .history +.ensime +.ensime_cache/ # Scala-IDE specific .scala_dependencies diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 000000000..ed5ff1bc5 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +dotty.epfl.ch
\ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml index d32781d9a..51578256b 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,5 +1,5 @@ title: Dotty Documentation -baseurl: "/dotty" +baseurl: "" theme: minima gems: - jekyll-toc diff --git a/docs/docs/index.md b/docs/docs/index.md index b8c5e9c20..6fc2b2739 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -11,9 +11,9 @@ pages. Index ----- -* [Blog](blog/) * Usage - [Migrating from Scala 2](usage/migrating.md) + - [Using Dotty with cbt](usage/cbt-projects.md) - [Using Dotty with sbt](usage/sbt-projects.md) * Contributing - [Getting Started](contributing/getting-started.md) details on how to run diff --git a/project/Build.scala b/project/Build.scala index 5ee082800..7c57bd862 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -64,9 +64,6 @@ object DottyBuild extends Build { lazy val dotty = project.in(file(".")). dependsOn(`dotty-interfaces`). settings( - // Disable scaladoc generation, makes publishLocal much faster - publishArtifact in packageDoc := false, - overrideScalaVersionSetting, // set sources to src/, tests to test/ and resources to resources/ diff --git a/sandbox/scalajs/hello.scala b/sandbox/scalajs/src/main/scala/hello.scala index bd4aa8cc5..bd4aa8cc5 100644 --- a/sandbox/scalajs/hello.scala +++ b/sandbox/scalajs/src/main/scala/hello.scala diff --git a/src/dotty/Pair.scala b/src/dotty/Pair.scala deleted file mode 100644 index 2322fe169..000000000 --- a/src/dotty/Pair.scala +++ /dev/null @@ -1,5 +0,0 @@ -package dotty - -class Pair[T, U](x: T, y: U) { - -} diff --git a/src/dotty/Singleton.scala b/src/dotty/Singleton.scala deleted file mode 100644 index 4ba57a12d..000000000 --- a/src/dotty/Singleton.scala +++ /dev/null @@ -1,5 +0,0 @@ -package dotty - -class Singleton { - -} diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index af34164dc..639dac930 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -433,11 +433,11 @@ object desugar { if (!mods.is(Implicit)) Nil else if (ctx.owner is Package) { - ctx.error("implicit classes may not be toplevel", cdef.pos) + ctx.error(TopLevelImplicitClass(cdef), cdef.pos) Nil } else if (isCaseClass) { - ctx.error("implicit classes may not be case classes", cdef.pos) + ctx.error(ImplicitCaseClass(cdef), cdef.pos) Nil } else @@ -497,7 +497,7 @@ object desugar { .withPos(mdef.pos) val ValDef(selfName, selfTpt, _) = tmpl.self val selfMods = tmpl.self.mods - if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", tmpl.self.pos) + if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos) val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(name)), tmpl.self.rhs) .withMods(selfMods) .withPos(tmpl.self.pos orElse tmpl.pos.startPos) @@ -931,7 +931,7 @@ object desugar { val arity = ts.length def tupleTypeRef = defn.TupleType(arity) if (arity > Definitions.MaxTupleArity) { - ctx.error(s"tuple too long (max allowed: ${Definitions.MaxTupleArity})", tree.pos) + ctx.error(TupleTooLong(ts), tree.pos) unitLiteral } else if (arity == 1) ts.head else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts) @@ -1051,7 +1051,7 @@ object desugar { elems foreach collect case Alternative(trees) => for (tree <- trees; (vble, _) <- getVariables(tree)) - ctx.error("illegal variable in pattern alternative", vble.pos) + ctx.error(IllegalVariableInPatternAlternative(), vble.pos) case Annotated(arg, _) => collect(arg) case InterpolatedString(_, segments) => diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 7fd818c25..ac3beaff4 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -94,9 +94,42 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp) // ----- Modifiers ----------------------------------------------------- + /** Mod is intended to record syntactic information about modifiers, it's + * NOT a replacement of FlagSet. + * + * For any query about semantic information, check `flags` instead. + */ + sealed abstract class Mod(val flags: FlagSet) extends Positioned - /** Modifiers and annotations for definitions - * @param flags The set flags + object Mod { + case class Private() extends Mod(Flags.Private) + + case class Protected() extends Mod(Flags.Protected) + + case class Val() extends Mod(Flags.EmptyFlags) + + case class Var() extends Mod(Flags.Mutable) + + case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag) + + case class Final() extends Mod(Flags.Final) + + case class Sealed() extends Mod(Flags.Sealed) + + case class Override() extends Mod(Flags.Override) + + case class Abstract() extends Mod(Flags.Abstract) + + case class Lazy() extends Mod(Flags.Lazy) + + case class Inline() extends Mod(Flags.Inline) + + case class Type() extends Mod(Flags.EmptyFlags) + } + + /** Modifiers and annotations for definitions + * + * @param flags The set flags * @param privateWithin If a private or protected has is followed by a * qualifier [q], the name q, "" as a typename otherwise. * @param annotations The annotations preceding the modifiers @@ -104,7 +137,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Modifiers ( flags: FlagSet = EmptyFlags, privateWithin: TypeName = tpnme.EMPTY, - annotations: List[Tree] = Nil) extends Positioned with Cloneable { + annotations: List[Tree] = Nil, + mods: List[Mod] = Nil) extends Positioned with Cloneable { def is(fs: FlagSet): Boolean = flags is fs def is(fc: FlagConjunction): Boolean = flags is fc @@ -120,7 +154,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { if (this.flags == flags) this else copy(flags = flags) - def withAddedAnnotation(annot: Tree): Modifiers = + def withAddedMod(mod: Mod): Modifiers = + if (mods.exists(_ eq mod)) this + else withMods(mods :+ mod) + + def withMods(ms: List[Mod]): Modifiers = + if (mods eq ms) this + else copy(mods = ms) + + def withAddedAnnotation(annot: Tree): Modifiers = if (annotations.exists(_ eq annot)) this else withAnnotations(annotations :+ annot) diff --git a/src/dotty/tools/dotc/core/Comments.scala b/src/dotty/tools/dotc/core/Comments.scala index 1cf5aec38..1e623db4d 100644 --- a/src/dotty/tools/dotc/core/Comments.scala +++ b/src/dotty/tools/dotc/core/Comments.scala @@ -9,6 +9,7 @@ import util.Positions._ import util.CommentParsing._ import util.Property.Key import parsing.Parsers.Parser +import reporting.diagnostic.messages.ProperDefinitionNotFound object Comments { val ContextDoc = new Key[ContextDocstrings] @@ -125,7 +126,7 @@ object Comments { val newName = (tree.name.show + "$" + codePos + "$doc").toTermName untpd.DefDef(newName, tree.tparams, tree.vparamss, tree.tpt, tree.rhs) case _ => - ctx.error("proper definition was not found in `@usecase`", codePos) + ctx.error(ProperDefinitionNotFound(), codePos) tree } } diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 3835d553c..0e155b9e1 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -162,7 +162,8 @@ trait ConstraintHandling { /** Solve constraint set for given type parameter `param`. * If `fromBelow` is true the parameter is approximated by its lower bound, * otherwise it is approximated by its upper bound. However, any occurrences - * of the parameter in a refinement somewhere in the bound are removed. + * of the parameter in a refinement somewhere in the bound are removed. Also + * wildcard types in bounds are approximated by their upper or lower bounds. * (Such occurrences can arise for F-bounded types). * The constraint is left unchanged. * @return the instantiating type @@ -174,6 +175,27 @@ trait ConstraintHandling { def apply(tp: Type) = mapOver { tp match { case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent + case tp: WildcardType => + val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds + // Try to instantiate the wildcard to a type that is known to conform to it. + // This means: + // If fromBelow is true, we minimize the type overall + // Hence, if variance < 0, pick the maximal safe type: bounds.lo + // (i.e. the whole bounds range is over the type) + // if variance > 0, pick the minimal safe type: bounds.hi + // (i.e. the whole bounds range is under the type) + // if variance == 0, pick bounds.lo anyway (this is arbitrary but in line with + // the principle that we pick the smaller type when in doubt). + // If fromBelow is false, we maximize the type overall and reverse the bounds + // if variance != 0. For variance == 0, we still minimize. + // In summary we pick the bound given by this table: + // + // variance | -1 0 1 + // ------------------------ + // from below | lo lo hi + // from above | hi lo lo + // + if (variance == 0 || fromBelow == (variance < 0)) bounds.lo else bounds.hi case _ => tp } } diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index b0f1f0c98..a105741f5 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -179,7 +179,7 @@ object Decorators { /** Formatter that adds syntax highlighting to all interpolated values */ def hl(args: Any*)(implicit ctx: Context): String = - new SyntaxFormatter(sc).assemble(args) + new SyntaxFormatter(sc).assemble(args).stripMargin } } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 50746c61d..541d66306 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -140,6 +140,16 @@ class Definitions { lazy val Sys_errorR = SysPackage.moduleClass.requiredMethodRef(nme.error) def Sys_error(implicit ctx: Context) = Sys_errorR.symbol + /** The `scalaShadowing` package is used to safely modify classes and + * objects in scala so that they can be used from dotty. They will + * be visible as members of the `scala` package, replacing any objects + * or classes with the same name. But their binary artifacts are + * in `scalaShadowing` so they don't clash with the same-named `scala` + * members at runtime. + */ + lazy val ScalaShadowingPackageVal = ctx.requiredPackage("scalaShadowing") + lazy val ScalaShadowingPackageClass = ScalaShadowingPackageVal.moduleClass.asClass + /** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter) * because after erasure the Any and AnyVal references get remapped to the Object methods * which would result in a double binding assertion failure. @@ -410,8 +420,6 @@ class Definitions { lazy val StringAdd_plusR = StringAddClass.requiredMethodRef(nme.raw.PLUS) def StringAdd_+(implicit ctx: Context) = StringAdd_plusR.symbol - lazy val PairType: TypeRef = ctx.requiredClassRef("dotty.Pair") - def PairClass(implicit ctx: Context) = PairType.symbol.asClass lazy val PartialFunctionType: TypeRef = ctx.requiredClassRef("scala.PartialFunction") def PartialFunctionClass(implicit ctx: Context) = PartialFunctionType.symbol.asClass lazy val AbstractPartialFunctionType: TypeRef = ctx.requiredClassRef("scala.runtime.AbstractPartialFunction") @@ -450,17 +458,17 @@ class Definitions { def StaticAnnotationClass(implicit ctx: Context) = StaticAnnotationType.symbol.asClass // Annotation classes - lazy val AliasAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Alias") + lazy val AliasAnnotType = ctx.requiredClassRef("scala.annotation.internal.Alias") def AliasAnnot(implicit ctx: Context) = AliasAnnotType.symbol.asClass - lazy val AnnotationDefaultAnnotType = ctx.requiredClassRef("dotty.annotation.internal.AnnotationDefault") + lazy val AnnotationDefaultAnnotType = ctx.requiredClassRef("scala.annotation.internal.AnnotationDefault") def AnnotationDefaultAnnot(implicit ctx: Context) = AnnotationDefaultAnnotType.symbol.asClass - lazy val BodyAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Body") + lazy val BodyAnnotType = ctx.requiredClassRef("scala.annotation.internal.Body") def BodyAnnot(implicit ctx: Context) = BodyAnnotType.symbol.asClass - lazy val ChildAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Child") + lazy val ChildAnnotType = ctx.requiredClassRef("scala.annotation.internal.Child") def ChildAnnot(implicit ctx: Context) = ChildAnnotType.symbol.asClass - lazy val CovariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.CovariantBetween") + lazy val CovariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.CovariantBetween") def CovariantBetweenAnnot(implicit ctx: Context) = CovariantBetweenAnnotType.symbol.asClass - lazy val ContravariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.ContravariantBetween") + lazy val ContravariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.ContravariantBetween") def ContravariantBetweenAnnot(implicit ctx: Context) = ContravariantBetweenAnnotType.symbol.asClass lazy val DeprecatedAnnotType = ctx.requiredClassRef("scala.deprecated") def DeprecatedAnnot(implicit ctx: Context) = DeprecatedAnnotType.symbol.asClass @@ -468,9 +476,9 @@ class Definitions { def ImplicitNotFoundAnnot(implicit ctx: Context) = ImplicitNotFoundAnnotType.symbol.asClass lazy val InlineAnnotType = ctx.requiredClassRef("scala.inline") def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass - lazy val InlineParamAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InlineParam") + lazy val InlineParamAnnotType = ctx.requiredClassRef("scala.annotation.internal.InlineParam") def InlineParamAnnot(implicit ctx: Context) = InlineParamAnnotType.symbol.asClass - lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("dotty.annotation.internal.InvariantBetween") + lazy val InvariantBetweenAnnotType = ctx.requiredClassRef("scala.annotation.internal.InvariantBetween") def InvariantBetweenAnnot(implicit ctx: Context) = InvariantBetweenAnnotType.symbol.asClass lazy val MigrationAnnotType = ctx.requiredClassRef("scala.annotation.migration") def MigrationAnnot(implicit ctx: Context) = MigrationAnnotType.symbol.asClass @@ -478,9 +486,9 @@ class Definitions { def NativeAnnot(implicit ctx: Context) = NativeAnnotType.symbol.asClass lazy val RemoteAnnotType = ctx.requiredClassRef("scala.remote") def RemoteAnnot(implicit ctx: Context) = RemoteAnnotType.symbol.asClass - lazy val RepeatedAnnotType = ctx.requiredClassRef("dotty.annotation.internal.Repeated") + lazy val RepeatedAnnotType = ctx.requiredClassRef("scala.annotation.internal.Repeated") def RepeatedAnnot(implicit ctx: Context) = RepeatedAnnotType.symbol.asClass - lazy val SourceFileAnnotType = ctx.requiredClassRef("dotty.annotation.internal.SourceFile") + lazy val SourceFileAnnotType = ctx.requiredClassRef("scala.annotation.internal.SourceFile") def SourceFileAnnot(implicit ctx: Context) = SourceFileAnnotType.symbol.asClass lazy val ScalaSignatureAnnotType = ctx.requiredClassRef("scala.reflect.ScalaSignature") def ScalaSignatureAnnot(implicit ctx: Context) = ScalaSignatureAnnotType.symbol.asClass @@ -510,7 +518,7 @@ class Definitions { def UncheckedStableAnnot(implicit ctx: Context) = UncheckedStableAnnotType.symbol.asClass lazy val UncheckedVarianceAnnotType = ctx.requiredClassRef("scala.annotation.unchecked.uncheckedVariance") def UncheckedVarianceAnnot(implicit ctx: Context) = UncheckedVarianceAnnotType.symbol.asClass - lazy val UnsafeNonvariantAnnotType = ctx.requiredClassRef("dotty.annotation.internal.UnsafeNonvariant") + lazy val UnsafeNonvariantAnnotType = ctx.requiredClassRef("scala.annotation.internal.UnsafeNonvariant") def UnsafeNonvariantAnnot(implicit ctx: Context) = UnsafeNonvariantAnnotType.symbol.asClass lazy val VolatileAnnotType = ctx.requiredClassRef("scala.volatile") def VolatileAnnot(implicit ctx: Context) = VolatileAnnotType.symbol.asClass @@ -781,6 +789,11 @@ class Definitions { if (!_isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() + + // Enter all symbols from the scalaShadowing package in the scala package + for (m <- ScalaShadowingPackageClass.info.decls) + ScalaPackageClass.enter(m) + _isInitialized = true } } diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 3f4433708..63fbc98dc 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -544,6 +544,9 @@ object Flags { /** An inline method */ final val InlineMethod = allOf(Inline, Method) + /** An inline parameter */ + final val InlineParam = allOf(Inline, Param) + /** A parameter or parameter accessor */ final val ParamOrAccessor = Param | ParamAccessor diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index b495f00d0..1980fe50d 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -965,28 +965,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * Test that the resulting bounds are still satisfiable. */ private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean = - ctx.mode.is(Mode.GADTflexible) && { - val tparam = tr.symbol - typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") - if (bound.isRef(tparam)) false - else bound match { - case bound: TypeRef - if bound.symbol.is(BindDefinedType) && ctx.gadt.bounds.contains(bound.symbol) && - !tr.symbol.is(BindDefinedType) => - // Avoid having pattern-bound types in gadt bounds, - // as these might be eliminated once the pattern is typechecked. - // Pattern-bound type symbols should be narrowed first, only if that fails - // should symbols in the environment be constrained. - narrowGADTBounds(bound, tr, !isUpper) - case _ => - val oldBounds = ctx.gadt.bounds(tparam) - val newBounds = - if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) - else TypeBounds(oldBounds.lo | bound, oldBounds.hi) - isSubType(newBounds.lo, newBounds.hi) && - { ctx.gadt.setBounds(tparam, newBounds); true } + ctx.mode.is(Mode.GADTflexible) && !frozenConstraint && { + val tparam = tr.symbol + typr.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") + if (bound.isRef(tparam)) false + else bound match { + case bound: TypeRef + if bound.symbol.is(BindDefinedType) && + ctx.gadt.bounds.contains(bound.symbol) && + !tr.symbol.is(BindDefinedType) => + // Avoid having pattern-bound types in gadt bounds, + // as these might be eliminated once the pattern is typechecked. + // Pattern-bound type symbols should be narrowed first, only if that fails + // should symbols in the environment be constrained. + narrowGADTBounds(bound, tr, !isUpper) + case _ => + val oldBounds = ctx.gadt.bounds(tparam) + val newBounds = + if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) + else TypeBounds(oldBounds.lo | bound, oldBounds.hi) + isSubType(newBounds.lo, newBounds.hi) && + { ctx.gadt.setBounds(tparam, newBounds); true } + } } - } // Tests around `matches` diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d242843e5..38913a7d0 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -239,9 +239,14 @@ object Types { /** The parts of this type which are type or term refs and which * satisfy predicate `p`. + * + * @param p The predicate to satisfy + * @param excludeLowerBounds If set to true, the lower bounds of abstract + * types will be ignored. */ - def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = - new NamedPartsAccumulator(p).apply(mutable.LinkedHashSet(), this) + def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false) + (implicit ctx: Context): collection.Set[NamedType] = + new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this) /** Map function `f` over elements of an AndType, rebuilding with function `g` */ def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match { @@ -615,13 +620,13 @@ object Types { /** The set of abstract term members of this type. */ final def abstractTermMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTermMembers") { memberDenots(abstractTermNameFilter, - (name, buf) => buf ++= member(name).altsWith(_ is Deferred)) + (name, buf) => buf ++= nonPrivateMember(name).altsWith(_ is Deferred)) } /** The set of abstract type members of this type. */ final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTypeMembers") { memberDenots(abstractTypeNameFilter, - (name, buf) => buf += member(name).asSingleDenotation) + (name, buf) => buf += nonPrivateMember(name).asSingleDenotation) } /** The set of abstract type members of this type. */ @@ -3710,7 +3715,8 @@ object Types { def apply(x: Boolean, tp: Type) = x || tp.isUnsafeNonvariant || foldOver(x, tp) } - class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] { + class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false) + (implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] { override def stopAtStatic = false def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x val seen: mutable.Set[Type] = mutable.Set() @@ -3723,7 +3729,8 @@ object Types { apply(foldOver(maybeAdd(x, tp), tp), tp.underlying) case tp: TypeRef => foldOver(maybeAdd(x, tp), tp) - case TypeBounds(_, hi) => + case TypeBounds(lo, hi) => + if (!excludeLowerBounds) apply(x, lo) apply(x, hi) case tp: ThisType => apply(x, tp.tref) @@ -3756,7 +3763,7 @@ object Types { object abstractTypeNameFilter extends NameFilter { def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = name.isTypeName && { - val mbr = pre.member(name) + val mbr = pre.nonPrivateMember(name) (mbr.symbol is Deferred) && mbr.info.isInstanceOf[RealTypeBounds] } } @@ -3773,7 +3780,7 @@ object Types { /** A filter for names of deferred term definitions of a given type */ object abstractTermNameFilter extends NameFilter { def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = - name.isTermName && (pre member name).hasAltWith(_.symbol is Deferred) + name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol is Deferred) } object typeNameFilter extends NameFilter { diff --git a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index 69fd63805..3ff7298ce 100644 --- a/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -12,6 +12,7 @@ import TastyName._ import TastyFormat._ class NameBuffer extends TastyBuffer(10000) { + import NameBuffer._ private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] @@ -40,13 +41,12 @@ class NameBuffer extends TastyBuffer(10000) { nameIndex(name) } - private def withLength(op: => Unit): Unit = { + private def withLength(op: => Unit, lengthWidth: Int = 1): Unit = { val lengthAddr = currentAddr - writeByte(0) + for (i <- 0 until lengthWidth) writeByte(0) op val length = currentAddr.index - lengthAddr.index - 1 - assert(length < 128) - putNat(lengthAddr, length, 1) + putNat(lengthAddr, length, lengthWidth) } def writeNameRef(ref: NameRef) = writeNat(ref.index) @@ -64,7 +64,9 @@ class NameBuffer extends TastyBuffer(10000) { withLength { writeNameRef(qualified); writeNameRef(selector) } case Signed(original, params, result) => writeByte(SIGNED) - withLength { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) } + withLength( + { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) }, + if ((params.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2) case Expanded(prefix, original) => writeByte(EXPANDED) withLength { writeNameRef(prefix); writeNameRef(original) } @@ -91,3 +93,9 @@ class NameBuffer extends TastyBuffer(10000) { } } } + +object NameBuffer { + private val maxIndexWidth = 3 // allows name indices up to 2^21. + private val payloadBitsPerByte = 7 // determined by nat encoding in TastyBuffer + private val maxNumInByte = (1 << payloadBitsPerByte) - 1 +} diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index ed7cf9e3f..b6a423dc7 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -21,6 +21,7 @@ import Symbols._ import ast.Trees._ import Decorators._ import StdNames._ +import dotty.tools.dotc.reporting.diagnostic.messages.IdentifierExpected import dotty.tools.dotc.util.SourceFile import util.Positions._ import annotation.switch @@ -131,7 +132,7 @@ object JavaParsers { def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) def makeParam(name: TermName, tpt: Tree): ValDef = - ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.PrivateLocalParamAccessor)) + ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.ParamAccessor)) def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined) = { val vparams = mapWithIndex(formals)((p, i) => makeSyntheticParam(i + 1, p)) @@ -230,7 +231,7 @@ object JavaParsers { case AppliedTypeTree(_, _) | Select(_, _) => tree case _ => - syntaxError("identifier expected", tree.pos) + syntaxError(IdentifierExpected(tree.show), tree.pos) errorTypeTree } } diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 507a2e80c..8768466a2 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -316,7 +316,7 @@ object Parsers { case id @ Select(qual, name) => cpy.Select(id)(qual, name.toTypeName) case _ => - syntaxError("identifier expected", tree.pos) + syntaxError(IdentifierExpected(tree.show), tree.pos) tree } @@ -334,7 +334,7 @@ object Parsers { try op finally { placeholderParams match { - case vd :: _ => syntaxError("unbound placeholder parameter", vd.pos) + case vd :: _ => syntaxError(UnboundPlaceholderParameter(), vd.pos) case _ => } placeholderParams = savedPlaceholderParams @@ -623,7 +623,7 @@ object Parsers { if (inPattern) Block(Nil, inBraces(pattern())) else expr() else { - syntaxErrorOrIncomplete("error in interpolated string: identifier or block expected") + ctx.error(InterpolatedStringError()) EmptyTree } }) @@ -692,7 +692,7 @@ object Parsers { else { for (t <- ts) if (t.isInstanceOf[ByNameTypeTree]) - syntaxError("no by-name parameter type allowed here", t.pos) + syntaxError(ByNameParameterNotSupported()) val tuple = atPos(start) { makeTupleOrParens(ts) } infixTypeRest(refinedTypeRest(withTypeRest(simpleTypeRest(tuple)))) } @@ -1079,7 +1079,8 @@ object Parsers { if (in.token != RPAREN) syntaxError("`_*' can be used only for last argument", uscoreStart) Typed(t, atPos(uscoreStart) { Ident(tpnme.WILDCARD_STAR) }) } else { - syntaxErrorOrIncomplete("`*' expected"); t + syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax()) + t } case AT if location != Location.InPattern => (t /: annotations())(Annotated) @@ -1097,8 +1098,9 @@ object Parsers { /** Expr ::= implicit Id `=>' Expr * BlockResult ::= implicit Id [`:' InfixType] `=>' Block */ - def implicitClosure(start: Int, location: Location.Value): Tree = { - val mods = atPos(start) { Modifiers(Implicit) } + 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) @@ -1177,7 +1179,7 @@ object Parsers { case _ => if (isLiteral) literal() else { - syntaxErrorOrIncomplete("illegal start of simple expression") + syntaxErrorOrIncomplete(IllegalStartSimpleExpr(tokenString(in.token))) errorTermTree } } @@ -1326,7 +1328,7 @@ object Parsers { if (in.token == YIELD) { in.nextToken(); ForYield(enums, expr()) } else if (in.token == DO) { in.nextToken(); ForDo(enums, expr()) } else { - if (!wrappedEnums) syntaxErrorOrIncomplete("`yield' or `do' expected") + if (!wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension()) ForDo(enums, expr()) } } @@ -1464,19 +1466,19 @@ object Parsers { /* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */ - private def flagOfToken(tok: Int): FlagSet = tok match { - case ABSTRACT => Abstract - case FINAL => Final - case IMPLICIT => ImplicitCommon - case INLINE => Inline - case LAZY => Lazy - case OVERRIDE => Override - case PRIVATE => Private - case PROTECTED => Protected - case SEALED => Sealed + private def modOfToken(tok: Int): Mod = tok match { + case ABSTRACT => Mod.Abstract() + case FINAL => Mod.Final() + case IMPLICIT => Mod.Implicit(ImplicitCommon) + case INLINE => Mod.Inline() + case LAZY => Mod.Lazy() + case OVERRIDE => Mod.Override() + case PRIVATE => Mod.Private() + case PROTECTED => Mod.Protected() + case SEALED => Mod.Sealed() } - /** Drop `private' modifier when followed by a qualifier. + /** Drop `private' modifier when followed by a qualifier. * Contract `abstract' and `override' to ABSOVERRIDE */ private def normalize(mods: Modifiers): Modifiers = @@ -1488,11 +1490,11 @@ object Parsers { mods private def addModifier(mods: Modifiers): Modifiers = { - val flag = flagOfToken(in.token) - if (mods is flag) syntaxError("repeated modifier") - val res = addFlag(mods, flag) - in.nextToken() - res + val tok = in.token + val mod = atPos(in.skipToken()) { modOfToken(tok) } + + if (mods is mod.flags) syntaxError(RepeatedModifier(mod.flags.toString)) + addMod(mods, mod) } private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean = ( @@ -1518,6 +1520,11 @@ object Parsers { } } + /** Always add the syntactic `mod`, but check and conditionally add semantic `mod.flags` + */ + def addMod(mods: Modifiers, mod: Mod): Modifiers = + addFlag(mods, mod.flags).withAddedMod(mod) + /** AccessQualifier ::= "[" (Id | this) "]" */ def accessQualifierOpt(mods: Modifiers): Modifiers = @@ -1614,10 +1621,10 @@ object Parsers { mods = atPos(start, in.offset) { if (in.token == TYPE) { - in.nextToken() - mods | Param | ParamAccessor + val mod = atPos(in.skipToken()) { Mod.Type() } + (mods | Param | ParamAccessor).withAddedMod(mod) } else { - if (mods.hasFlags) syntaxError("`type' expected") + if (mods.hasFlags) syntaxError(TypeParamsTypeExpected(mods, ident())) mods | Param | PrivateLocal } } @@ -1659,7 +1666,7 @@ object Parsers { * Param ::= id `:' ParamType [`=' Expr] */ def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = { - var implicitFlag = EmptyFlags + var implicitMod: Mod = null var firstClauseOfCaseClass = ofCaseClass var implicitOffset = -1 // use once def param(): ValDef = { @@ -1670,11 +1677,11 @@ object Parsers { mods = atPos(start, in.offset) { if (in.token == VAL) { - in.nextToken() - mods + val mod = atPos(in.skipToken()) { Mod.Val() } + mods.withAddedMod(mod) } else if (in.token == VAR) { - in.nextToken() - addFlag(mods, Mutable) + val mod = atPos(in.skipToken()) { Mod.Var() } + addMod(mods, mod) } else { if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty) syntaxError("`val' or `var' expected") @@ -1696,7 +1703,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 (!implicitFlag.isEmpty) + else if (implicitMod != null) syntaxError("implicit parameters may not be call-by-name") } paramType() @@ -1708,15 +1715,16 @@ object Parsers { mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset))) implicitOffset = -1 } - ValDef(name, tpt, default).withMods(addFlag(mods, implicitFlag)) + if (implicitMod != null) mods = addMod(mods, implicitMod) + ValDef(name, tpt, default).withMods(mods) } } def paramClause(): List[ValDef] = inParens { if (in.token == RPAREN) Nil else { if (in.token == IMPLICIT) { - implicitOffset = in.skipToken() - implicitFlag = Implicit + implicitOffset = in.offset + implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) } } commaSeparated(param) } @@ -1726,7 +1734,7 @@ object Parsers { if (in.token == LPAREN) paramClause() :: { firstClauseOfCaseClass = false - if (implicitFlag.isEmpty) clauses() else Nil + if (implicitMod == null) clauses() else Nil } else Nil } @@ -1735,8 +1743,8 @@ object Parsers { if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods is Implicit)))) { in.token match { case LBRACKET => syntaxError("no type parameters allowed here") - case EOF => incompleteInputError("auxiliary constructor needs non-implicit parameter list") - case _ => syntaxError("auxiliary constructor needs non-implicit parameter list", start) + case EOF => incompleteInputError(AuxConstructorNeedsNonImplicitParameter()) + case _ => syntaxError(AuxConstructorNeedsNonImplicitParameter(), start) } } result @@ -1819,9 +1827,13 @@ object Parsers { */ def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match { case VAL => - patDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) + val mod = atPos(in.skipToken()) { Mod.Val() } + val mods1 = mods.withAddedMod(mod) + patDefOrDcl(start, mods1, in.getDocComment(start)) case VAR => - patDefOrDcl(start, posMods(start, addFlag(mods, Mutable)), in.getDocComment(start)) + val mod = atPos(in.skipToken()) { Mod.Var() } + val mod1 = addMod(mods, mod) + patDefOrDcl(start, mod1, in.getDocComment(start)) case DEF => defDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) case TYPE => @@ -1898,7 +1910,7 @@ object Parsers { else EmptyTree } else { - if (!isExprIntro) syntaxError("missing return type", in.lastOffset) + if (!isExprIntro) syntaxError(MissingReturnType(), in.lastOffset) accept(EQUALS) expr() } @@ -2061,7 +2073,7 @@ object Parsers { def templateBody(): (ValDef, List[Tree]) = { val r = inDefScopeBraces { templateStatSeq() } if (in.token == WITH) { - syntaxError("early definitions are not supported; use trait parameters instead") + syntaxError(EarlyDefinitionsNotSupported()) in.nextToken() template(emptyConstructor) } @@ -2184,8 +2196,11 @@ object Parsers { stats.toList } - def localDef(start: Int, implicitFlag: FlagSet): Tree = - defOrDcl(start, addFlag(defAnnotsMods(localModifierTokens), implicitFlag)) + 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) + defOrDcl(start, mods) + } /** BlockStatSeq ::= { BlockStat semi } [ResultExpr] * BlockStat ::= Import @@ -2205,9 +2220,10 @@ object Parsers { stats += expr(Location.InBlock) else if (isDefIntro(localModifierTokens)) if (in.token == IMPLICIT) { - val start = in.skipToken() - if (isIdent) stats += implicitClosure(start, Location.InBlock) - else stats += localDef(start, ImplicitCommon) + 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)) } else { stats += localDef(in.offset, EmptyFlags) } diff --git a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 9cfac4801..b986b3cc8 100644 --- a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -4,12 +4,12 @@ package reporting package diagnostic import dotc.core._ -import Contexts.Context, Decorators._, Symbols._, Names._, Types._ +import Contexts.Context, Decorators._, Symbols._, Names._, NameOps._, Types._ import util.{SourceFile, NoSource} import util.{SourcePosition, NoSourcePosition} import config.Settings.Setting import interfaces.Diagnostic.{ERROR, WARNING, INFO} -import printing.SyntaxHighlighting._ +import printing.Highlighting._ import printing.Formatting object messages { @@ -77,8 +77,15 @@ object messages { * EmptyCatchBlock(tree).warning(pos) // res: Warning * ``` */ - import dotc.ast.Trees._ - import dotc.ast.untpd + import ast.Trees._ + import ast.untpd + import ast.tpd + + /** Helper methods for messages */ + def implicitClassRestrictionsText(implicit ctx: Context) = + hl"""|${NoColor("For a full list of restrictions on implicit classes visit")} + |${Blue("http://docs.scala-lang.org/overviews/core/implicit-classes.html")}""" + // Syntax Errors ---------------------------------------------------------- // abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: Int)(implicit ctx: Context) @@ -113,7 +120,7 @@ object messages { |$code2 | |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it - |correctly handles transfer functions like ${"return"}.""".stripMargin + |correctly handles transfer functions like ${"return"}.""" } } @@ -122,7 +129,7 @@ object messages { val kind = "Syntax" val msg = hl"""|The ${"catch"} block does not contain a valid expression, try - |adding a case like - `${"case e: Exception =>"}` to the block""".stripMargin + |adding a case like - `${"case e: Exception =>"}` to the block""" } case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -130,7 +137,7 @@ object messages { val kind = "Syntax" val msg = hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting - |its body in a block; no exceptions are handled.""".stripMargin + |its body in a block; no exceptions are handled.""" } case class DeprecatedWithOperator()(implicit ctx: Context) @@ -141,7 +148,7 @@ object messages { val explanation = hl"""|Dotty introduces intersection types - `&' types. These replace the |use of the ${"with"} keyword. There are a few differences in - |semantics between intersection types and using `${"with"}'.""".stripMargin + |semantics between intersection types and using `${"with"}'.""" } case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -153,7 +160,7 @@ object messages { val explanation = hl"""|${cdef.name} must have at least one parameter list, if you would rather |have a singleton representation of ${cdef.name}, use a "${"case object"}". - |Or, add an explicit `()' as a parameter list to ${cdef.name}.""".stripMargin + |Or, add an explicit `()' as a parameter list to ${cdef.name}.""" } @@ -181,7 +188,7 @@ object messages { | |$caseDef | - |`${bind.name}` is not unique. Rename one of the bound variables!""".stripMargin + |`${bind.name}` is not unique. Rename one of the bound variables!""" } } @@ -192,7 +199,7 @@ object messages { val explanation = { hl"""|An identifier for `$treeKind$name` is missing. This means that something - |has either been misspelt or you're forgetting an import""".stripMargin + |has either been misspelt or you're forgetting an import""" } } @@ -274,4 +281,477 @@ object messages { val explanation = "" } + + case class EarlyDefinitionsNotSupported()(implicit ctx:Context) + extends Message(9) { + val kind = "Syntax" + val msg = "early definitions are not supported; use trait parameters instead" + + val explanation = { + val code1 = + """|trait Logging { + | val f: File + | f.open() + | onExit(f.close()) + | def log(msg: String) = f.write(msg) + |} + | + |class B extends Logging { + | val f = new File("log.data") // triggers a NullPointerException + |} + | + |// early definition gets around the NullPointerException + |class C extends { + | val f = new File("log.data") + |} with Logging""".stripMargin + + val code2 = + """|trait Logging(f: File) { + | f.open() + | onExit(f.close()) + | def log(msg: String) = f.write(msg) + |} + | + |class C extends Logging(new File("log.data"))""".stripMargin + + hl"""|Earlier versions of Scala did not support trait parameters and "early + |definitions" (also known as "early initializers") were used as an alternative. + | + |Example of old syntax: + | + |$code1 + | + |The above code can now be written as: + | + |$code2 + |""" + } + } + + case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context) + extends Message(10) { + val kind = "Syntax" + val msg = hl"""An ${"implicit class"} may not be top-level""" + + val explanation = { + val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef + val exampleArgs = + constr0.vparamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") + def defHasBody[T] = impl.body.exists(!_.isEmpty) + val exampleBody = if (defHasBody) "{\n ...\n }" else "" + hl"""|There may not be any method, member or object in scope with the same name as + |the implicit class and a case class automatically gets a companion object with + |the same name created by the compiler which would cause a naming conflict if it + |were allowed. + | + |""" + implicitClassRestrictionsText + hl"""| + | + |To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class + |from the object at the use site if needed, for example: + | + |object Implicits { + | implicit class ${cdef.name}($exampleArgs)$exampleBody + |} + | + |// At the use site: + |import Implicits.${cdef.name}""" + } + } + + case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context) + extends Message(11) { + val kind = "Syntax" + val msg = hl"""A ${"case class"} may not be defined as ${"implicit"}""" + + val explanation = + hl"""|implicit classes may not be case classes. Instead use a plain class: + | + |implicit class ${cdef.name}... + | + |""" + implicitClassRestrictionsText + } + + case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context) + extends Message(12) { + val kind = "Syntax" + val msg = hl"""${"object"}s must not have a self ${"type"}""" + + val explanation = { + val untpd.ModuleDef(name, tmpl) = mdef + val ValDef(_, selfTpt, _) = tmpl.self + hl"""|${"object"}s must not have a self ${"type"}: + | + |Consider these alternative solutions: + | - Create a trait or a class instead of an object + | - Let the object extend a trait containing the self type: + | + | object $name extends ${selfTpt.show}""" + } + } + + case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context) + extends Message(13) { + import Definitions.MaxTupleArity + val kind = "Syntax" + val msg = hl"""A ${"tuple"} cannot have more than ${MaxTupleArity} members""" + + val explanation = { + val members = ts.map(_.showSummary).grouped(MaxTupleArity) + val nestedRepresentation = members.map(_.mkString(", ")).mkString(")(") + hl"""|This restriction will be removed in the future. + |Currently it is possible to use nested tuples when more than $MaxTupleArity are needed, for example: + | + |((${nestedRepresentation}))""" + } + } + + case class RepeatedModifier(modifier: String)(implicit ctx:Context) + extends Message(14) { + val kind = "Syntax" + val msg = hl"""repeated modifier $modifier""" + + val explanation = { + val code1 = hl"""private private val Origin = Point(0, 0)""" + val code2 = hl"""private final val Origin = Point(0, 0)""" + hl"""This happens when you accidentally specify the same modifier twice. + | + |Example: + | + |$code1 + | + |instead of + | + |$code2 + | + |""" + } + } + + case class InterpolatedStringError()(implicit ctx:Context) + extends Message(15) { + val kind = "Syntax" + val msg = "error in interpolated string: identifier or block expected" + val explanation = { + val code1 = "s\"$new Point(0, 0)\"" + val code2 = "s\"${new Point(0, 0)}\"" + hl"""|This usually happens when you forget to place your expressions inside curly braces. + | + |$code1 + | + |should be written as + | + |$code2 + |""" + } + } + + case class UnboundPlaceholderParameter()(implicit ctx:Context) + extends Message(16) { + val kind = "Syntax" + val msg = "unbound placeholder parameter; incorrect use of `_`" + val explanation = + hl"""|The `_` placeholder syntax was used where it could not be bound. + |Consider explicitly writing the variable binding. + | + |This can be done by replacing `_` with a variable (eg. `x`) + |and adding ${"x =>"} where applicable. + | + |Example before: + | + |${"{ _ }"} + | + |Example after: + | + |${"x => { x }"} + | + |Another common occurrence for this error is defining a val with `_`: + | + |${"val a = _"} + | + |But this val definition isn't very useful, it can never be assigned + |another value. And thus will always remain uninitialized. + |Consider replacing the ${"val"} with ${"var"}: + | + |${"var a = _"} + | + |Note that this use of `_` is not placeholder syntax, + |but an uninitialized var definition""" + } + + case class IllegalStartSimpleExpr(illegalToken: String)(implicit ctx: Context) + extends Message(17) { + val kind = "Syntax" + val msg = "illegal start of simple expression" + val explanation = { + hl"""|An expression yields a value. In the case of the simple expression, this error + |commonly occurs when there's a missing parenthesis or brace. The reason being + |that a simple expression is one of the following: + | + |- Block + |- Expression in parenthesis + |- Identifier + |- Object creation + |- Literal + | + |which cannot start with ${Red(illegalToken)}.""" + } + } + + case class MissingReturnType()(implicit ctx:Context) extends Message(18) { + val kind = "Syntax" + val msg = "missing return type" + val explanation = + hl"""|An abstract declaration must have a return type. For example: + | + |trait Shape { + | def area: Double // abstract declaration returning a ${"Double"} + |}""" + } + + case class YieldOrDoExpectedInForComprehension()(implicit ctx: Context) + extends Message(19) { + val kind = "Syntax" + val msg = hl"${"yield"} or ${"do"} expected" + + val explanation = + hl"""|When the enumerators in a for comprehension are not placed in parentheses or + |braces, a ${"do"} or ${"yield"} statement is required after the enumerators + |section of the comprehension. + | + |You can save some keystrokes by omitting the parentheses and writing + | + |${"val numbers = for i <- 1 to 3 yield i"} + | + | instead of + | + |${"val numbers = for (i <- 1 to 3) yield i"} + | + |but the ${"yield"} keyword is still required. + | + |For comprehensions that simply perform a side effect without yielding anything + |can also be written without parentheses but a ${"do"} keyword has to be + |included. For example, + | + |${"for (i <- 1 to 3) println(i)"} + | + |can be written as + | + |${"for i <- 1 to 3 do println(i) // notice the 'do' keyword"} + | + |""" + } + + case class ProperDefinitionNotFound()(implicit ctx: Context) + extends Message(20) { + val kind = "Definition Not Found" + val msg = hl"""Proper definition was not found in ${"@usecase"}""" + + val explanation = { + val noUsecase = + "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That" + + val usecase = + """|/** Map from List[A] => List[B] + | * + | * @usecase def map[B](f: A => B): List[B] + | */ + |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That + |""".stripMargin + + hl"""|Usecases are only supported for ${"def"}s. They exist because with Scala's + |advanced type-system, we sometimes end up with seemingly scary signatures. + |The usage of these methods, however, needs not be - for instance the `map` + |function + | + |${"List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)"} + | + |is easy to understand and use - but has a rather bulky signature: + | + |$noUsecase + | + |to mitigate this and ease the usage of such functions we have the ${"@usecase"} + |annotation for docstrings. Which can be used like this: + | + |$usecase + | + |When creating the docs, the signature of the method is substituted by the + |usecase and the compiler makes sure that it is valid. Because of this, you're + |only allowed to use ${"def"}s when defining usecases.""" + } + } + + case class ByNameParameterNotSupported()(implicit ctx: Context) + extends Message(21) { + val kind = "Syntax" + val msg = "By-name parameter type not allowed here." + + val explanation = + hl"""|By-name parameters act like functions that are only evaluated when referenced, + |allowing for lazy evaluation of a parameter. + | + |An example of using a by-name parameter would look like: + |${"def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function"} + | + |An example of the syntax of passing an actual function as a parameter: + |${"def func(f: (Boolean => Boolean)) = f(true)"} + | + |or: + | + |${"def func(f: Boolean => Boolean) = f(true)"} + | + |And the usage could be as such: + |${"func(bool => // do something...)"} + |""" + } + + case class WrongNumberOfArgs(fntpe: Type, argKind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) + extends Message(22) { + val kind = "Syntax" + + private val expectedCount = expectedArgs.length + private val actualCount = actual.length + private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" + + //TODO add def simpleParamName to TypeParamInfo + private val expectedArgString = fntpe + .widen.typeParams + .map(_.paramName.unexpandedName.show) + .mkString("[", ", ", "]") + + private val actualArgString = actual.map(_.show).mkString("[", ", ", "]") + + private val prettyName = fntpe.termSymbol match { + case NoSymbol => fntpe.show + case symbol => symbol.showFullName + } + + val msg = + hl"""|${NoColor(msgPrefix)} ${argKind} arguments for $prettyName$expectedArgString + |expected: $expectedArgString + |actual: $actualArgString""".stripMargin + + val explanation = { + val tooManyTypeParams = + """|val tuple2: (Int, String) = (1, "one") + |val list: List[(Int, String)] = List(tuple2)""".stripMargin + + if (actualCount > expectedCount) + hl"""|You have supplied too many type parameters + | + |For example List takes a single type parameter (List[A]) + |If you need to hold more types in a list then you need to combine them + |into another data type that can contain the number of types you need, + |In this example one solution would be to use a Tuple: + | + |${tooManyTypeParams}""" + else + hl"""|You have not supplied enough type parameters + |If you specify one type parameter then you need to specify every type parameter.""" + } + } + + case class IllegalVariableInPatternAlternative()(implicit ctx: Context) + extends Message(23) { + val kind = "Syntax" + val msg = "Variables are not allowed in alternative patterns" + val explanation = { + val varInAlternative = + """|def g(pair: (Int,Int)): Int = pair match { + | case (1, n) | (n, 1) => n + | case _ => 0 + |}""".stripMargin + + val fixedVarInAlternative = + """|def g(pair: (Int,Int)): Int = pair match { + | case (1, n) => n + | case (n, 1) => n + | case _ => 0 + |}""".stripMargin + + hl"""|Variables are not allowed within alternate pattern matches. You can workaround + |this issue by adding additional cases for each alternative. For example, the + |illegal function: + | + |$varInAlternative + |could be implemented by moving each alternative into a separate case: + | + |$fixedVarInAlternative""" + } + } + + case class TypeParamsTypeExpected(mods: untpd.Modifiers, identifier: TermName)(implicit ctx: Context) + extends Message(24) { + val kind = "Syntax" + val msg = hl"""Expected ${"type"} keyword for type parameter $identifier""" + val explanation = + hl"""|This happens when you add modifiers like ${"private"} or ${"protected"} + |to your type parameter definition without adding the ${"type"} keyword. + | + |Add ${"type"} to your code, e.g.: + |${s"trait A[${mods.flags} type $identifier]"} + |""" + } + + case class IdentifierExpected(identifier: String)(implicit ctx: Context) + extends Message(25) { + val kind = "Syntax" + val msg = "identifier expected" + val explanation = { + val wrongIdentifier = s"def foo: $identifier = {...}" + val validIdentifier = s"def foo = {...}" + hl"""|An identifier expected, but `$identifier` found. This could be because + |`$identifier` is not a valid identifier. As a workaround, the compiler could + |infer the type for you. For example, instead of: + | + |$wrongIdentifier + | + |Write your code like: + | + |$validIdentifier + | + |""" + } + } + + case class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) + extends Message(26) { + val kind = "Syntax" + val msg = "auxiliary constructor needs non-implicit parameter list" + val explanation = + hl"""|Only the primary constructor is allowed an ${"implicit"} parameter list; + |auxiliary constructors need non-implicit parameter lists. When a primary + |constructor has an implicit argslist, auxiliary constructors that call the + |primary constructor must specify the implicit value. + | + |To resolve this issue check for: + | - forgotten parenthesis on ${"this"} (${"def this() = { ... }"}) + | - auxiliary constructors specify the implicit value + |""" + } + + case class IncorrectRepeatedParameterSyntax()(implicit ctx: Context) extends Message(27) { + val kind = "Syntax" + val msg = "'*' expected" + val explanation = + hl"""|Expected * in '_*' operator. + | + |The '_*' operator can be used to supply a sequence-based argument + |to a method with a variable-length or repeated parameter. It is used + |to expand the sequence to a variable number of arguments, such that: + |func(args: _*) would expand to func(arg1, arg2 ... argN). + | + |Below is an example of how a method with a variable-length + |parameter can be declared and used. + | + |Squares the arguments of a variable-length parameter: + |${"def square(args: Int*) = args.map(a => a * a)"} + | + |Usage: + |${"square(1, 2, 3) // res0: List[Int] = List(1, 4, 9)"} + | + |Secondary Usage with '_*': + |${"val ints = List(2, 3, 4) // ints: List[Int] = List(2, 3, 4)"} + |${"square(ints: _*) // res1: List[Int] = List(4, 9, 16)"} + |""".stripMargin + } } diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 62a21198d..5ae4e8a54 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -32,6 +32,9 @@ import SymUtils._ * in [[ElimErasedValueType]]. * This is different from the implementation of value classes in Scala 2 * (see SIP-15) which uses `asInstanceOf` which does not typecheck. + * + * Finally, if the constructor of a value class is private pr protected + * it is widened to public. */ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer => @@ -96,11 +99,18 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful case _ => moduleClassSym } - case ref: SymDenotation - if isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnot) => - val ref1 = ref.copySymDenotation() - ref1.removeAnnotation(defn.TailrecAnnot) - ref1 + case ref: SymDenotation => + if (isMethodWithExtension(ref) && ref.hasAnnotation(defn.TailrecAnnot)) { + val ref1 = ref.copySymDenotation() + ref1.removeAnnotation(defn.TailrecAnnot) + ref1 + } + else if (ref.isConstructor && isDerivedValueClass(ref.owner) && ref.is(AccessFlags)) { + val ref1 = ref.copySymDenotation() + ref1.resetFlag(AccessFlags) + ref1 + } + else ref case _ => ref } diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 49c0eabec..8636d5084 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -240,17 +240,21 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { val isDefined = extractorMemberType(prev.tpe, nme.isDefined) if ((isDefined isRef defn.BooleanClass) && getTp.exists) { - val tmpSym = freshSym(prev.pos, prev.tpe, "o") - val prevValue = ref(tmpSym).select("get".toTermName).ensureApplied + // 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) - Block( - 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(nme.isDefined), - Block(List(ValDef(b.asTerm, prevValue)), next) - ) + val tmpSym = freshSym(prev.pos, prev.tpe, "o") + val prevValue = ref(tmpSym).select(getDenot.symbol).ensureApplied + + Block( + 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), + 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))) diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 49727e70b..7e51635e5 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -261,6 +261,19 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tpe => tpe } ) + case Import(expr, selectors) => + val exprTpe = expr.tpe + def checkIdent(ident: Ident): Unit = { + val name = ident.name.asTermName.encode + if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists) + ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos) + } + selectors.foreach { + case ident: Ident => checkIdent(ident) + case Thicket((ident: Ident) :: _) => checkIdent(ident) + case _ => + } + super.transform(tree) case tree => super.transform(tree) } diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index a7a8ac177..dc4454439 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -143,7 +143,11 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete newOwners = label :: Nil ).transform(rhsSemiTransformed) }) - Block(List(labelDef), ref(label).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol))))) + val callIntoLabel = ( + if (dd.tparams.isEmpty) ref(label) + else ref(label).appliedToTypes(dd.tparams.map(_.tpe)) + ).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol)))) + Block(List(labelDef), callIntoLabel) }} else { if (mandatory) ctx.error( "TailRec optimisation not applicable, method not tail recursive", diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 753475d84..dbfc89f6c 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -469,10 +469,13 @@ trait Checking { /** Check that `tree` is a pure expression of constant type */ def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit = - tree.tpe.widenTermRefExpr match { - case tp: ConstantType if isPureExpr(tree) => // ok - case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok - case _ => ctx.error(em"$what must be a constant expression or a function", tree.pos) + tree.tpe match { + case tp: TermRef if tp.symbol.is(InlineParam) => // ok + case tp => tp.widenTermRefExpr match { + case tp: ConstantType if isPureExpr(tree) => // ok + case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok + case _ => ctx.error(em"$what must be a constant expression or a function", tree.pos) + } } /** Check that class does not define same symbol twice */ diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 1d22dc646..a18c83ff8 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -49,8 +49,8 @@ object ErrorReporting { errorMsg(ex.show, ctx) } - def wrongNumberOfArgs(fntpe: Type, kind: String, expected: Int, pos: Position)(implicit ctx: Context) = - errorType(em"wrong number of ${kind}arguments for $fntpe, expected: $expected", pos) + 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) class Errors(implicit ctx: Context) { diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index d6cf7fb2b..f3dceea71 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -343,7 +343,9 @@ trait ImplicitRunInfo { self: RunInfo => } tp.classSymbols(liftingCtx) foreach addClassScope case _ => - for (part <- tp.namedPartsWith(_.isType)) + // We exclude lower bounds to conform to SLS 7.2: + // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound" + for (part <- tp.namedPartsWith(_.isType, excludeLowerBounds = true)) comps ++= iscopeRefs(part) } comps diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 9a1a42e44..aede4974a 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -223,7 +223,6 @@ object Inferencing { constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}") val vs = variances(tp, qualifies) - var changed = false val hasUnreportedErrors = ctx.typerState.reporter match { case r: StoreReporter if r.hasErrors => true case _ => false @@ -253,17 +252,13 @@ object Inferencing { if (v != 0) { typr.println(s"interpolate ${if (v == 1) "co" else "contra"}variant ${tvar.show} in ${tp.show}") tvar.instantiate(fromBelow = v == 1) - changed = true } } - if (changed) // instantiations might have uncovered new typevars to interpolate - interpolateUndetVars(tree, ownedBy) - else - for (tvar <- constraint.uninstVars) - if (!(vs contains tvar) && qualifies(tvar)) { - typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show}") - tvar.instantiate(fromBelow = true) - } + for (tvar <- constraint.uninstVars) + if (!(vs contains tvar) && qualifies(tvar)) { + typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show} / $tp") + tvar.instantiate(fromBelow = true) + } } if (constraint.uninstVars exists qualifies) interpolate() } @@ -306,12 +301,16 @@ object Inferencing { * we want to instantiate U to x.type right away. No need to wait further. */ private def variances(tp: Type, include: TypeVar => Boolean)(implicit ctx: Context): VarianceMap = Stats.track("variances") { - val accu = new TypeAccumulator[VarianceMap] { + val constraint = ctx.typerState.constraint + + object accu extends TypeAccumulator[VarianceMap] { + def setVariance(v: Int) = variance = v def apply(vmap: VarianceMap, t: Type): VarianceMap = t match { - case t: TypeVar if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) => + case t: TypeVar + if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) => val v = vmap(t) if (v == null) vmap.updated(t, variance) - else if (v == variance) vmap + else if (v == variance || v == 0) vmap else vmap.updated(t, 0) case _ => foldOver(vmap, t) @@ -319,7 +318,37 @@ object Inferencing { override def applyToPrefix(vmap: VarianceMap, t: NamedType) = apply(vmap, t.prefix) } - accu(SimpleMap.Empty, tp) + + /** Include in `vmap` type variables occurring in the constraints of type variables + * already in `vmap`. Specifically: + * - if `tvar` is covariant in `vmap`, include all variables in its lower bound + * (because they influence the minimal solution of `tvar`), + * - if `tvar` is contravariant in `vmap`, include all variables in its upper bound + * at flipped variances (because they influence the maximal solution of `tvar`), + * - if `tvar` is nonvariant in `vmap`, include all variables in its upper and lower + * bounds as non-variant. + * Do this in a fixpoint iteration until `vmap` stabilizes. + */ + def propagate(vmap: VarianceMap): VarianceMap = { + var vmap1 = vmap + def traverse(tp: Type) = { vmap1 = accu(vmap1, tp) } + vmap.foreachBinding { (tvar, v) => + val param = tvar.origin + val e = constraint.entry(param) + accu.setVariance(v) + if (v >= 0) { + traverse(e.bounds.lo) + constraint.lower(param).foreach(p => traverse(constraint.typeVarOfParam(p))) + } + if (v <= 0) { + traverse(e.bounds.hi) + constraint.upper(param).foreach(p => traverse(constraint.typeVarOfParam(p))) + } + } + if (vmap1 eq vmap) vmap else propagate(vmap1) + } + + propagate(accu(SimpleMap.Empty, tp)) } } diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index a2bb9b8d0..026015d1d 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -487,7 +487,7 @@ object RefChecks { // abstract method, and a cursory examination of the difference reveals // something obvious to us, let's make it more obvious to them. val abstractParams = underlying.info.firstParamTypes - val matchingName = clazz.info.member(underlying.name).alternatives + val matchingName = clazz.info.nonPrivateMember(underlying.name).alternatives val matchingArity = matchingName filter { m => !m.symbol.is(Deferred) && m.info.firstParamTypes.length == abstractParams.length diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 861847b11..b1fed308c 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -314,7 +314,7 @@ 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, "", ptypes.length, tree.pos) + else wrongNumberOfArgs(fn.tpe, "", fntpe.typeParams, args, 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.paramNames.length, tree.pos) + else wrongNumberOfArgs(fn.tpe, "type", pt.typeParams, args, tree.pos) } case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) @@ -451,7 +451,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.length, tree.pos) + else wrongNumberOfArgs(tycon.tpe, "type", tparams, args, tree.pos) tree.withType(ownType) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index f6d53f1e2..c283f43cf 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -577,16 +577,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def escapingRefs(block: Tree, localSyms: => List[Symbol])(implicit ctx: Context): collection.Set[NamedType] = { - var hoisted: Set[Symbol] = Set() lazy val locals = localSyms.toSet - def leakingTypes(tp: Type): collection.Set[NamedType] = - tp namedPartsWith (tp => locals.contains(tp.symbol)) - def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty - def classLeaks(sym: ClassSymbol): Boolean = - (ctx.owner is Method) || // can't hoist classes out of method bodies - (sym.info.parents exists typeLeaks) || - (sym.info.decls.toList exists (t => typeLeaks(t.info))) - leakingTypes(block.tpe) + block.tpe namedPartsWith (tp => locals.contains(tp.symbol)) } /** Check that expression's type can be expressed without references to locally defined @@ -1038,7 +1030,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.length, tree.pos) + wrongNumberOfArgs(tpt1.tpe, "type", tparams, args, tree.pos) args = args.take(tparams.length) } def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala index 595ea34ca..aad4995d8 100644 --- a/src/dotty/tools/dotc/util/SourcePosition.scala +++ b/src/dotty/tools/dotc/util/SourcePosition.scala @@ -29,7 +29,7 @@ extends interfaces.SourcePosition { source.lineContent(source.lineToOffset(lineNumber)) def beforeAndAfterPoint: (List[Int], List[Int]) = - lineOffsets.partition(_ < point) + lineOffsets.partition(_ <= point) /** The column of the position, starting at 0 */ def column: Int = source.column(point) diff --git a/src/dotty/annotation/internal/Alias.scala b/src/scala/annotation/internal/Alias.scala index 8be83960f..e3f56e70c 100644 --- a/src/dotty/annotation/internal/Alias.scala +++ b/src/scala/annotation/internal/Alias.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/AnnotationDefault.scala b/src/scala/annotation/internal/AnnotationDefault.scala index 7409b2f96..5280d091c 100644 --- a/src/dotty/annotation/internal/AnnotationDefault.scala +++ b/src/scala/annotation/internal/AnnotationDefault.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/Body.scala b/src/scala/annotation/internal/Body.scala index 7e26b02f2..b6aa0c0fb 100644 --- a/src/dotty/annotation/internal/Body.scala +++ b/src/scala/annotation/internal/Body.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/Child.scala b/src/scala/annotation/internal/Child.scala index 9295de73e..c90871945 100644 --- a/src/dotty/annotation/internal/Child.scala +++ b/src/scala/annotation/internal/Child.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation @@ -9,7 +9,7 @@ import scala.annotation.Annotation * case class B() extends A * case class C() extends A * - * Then the class symbol `A` would carry the annotations + * Then the class symbol `A` would carry the annotations * `@Child[Bref] @Child[Cref]` where `Bref`, `Cref` are TypeRefs * referring to the class symbols of `B` and `C` */ diff --git a/src/dotty/annotation/internal/InlineParam.scala b/src/scala/annotation/internal/InlineParam.scala index a144f9edb..0b3649e89 100644 --- a/src/dotty/annotation/internal/InlineParam.scala +++ b/src/scala/annotation/internal/InlineParam.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/Repeated.scala b/src/scala/annotation/internal/Repeated.scala index 24adc051f..75eb3bc25 100644 --- a/src/dotty/annotation/internal/Repeated.scala +++ b/src/scala/annotation/internal/Repeated.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/SourceFile.scala b/src/scala/annotation/internal/SourceFile.scala index c49fc2c8d..b203869cf 100644 --- a/src/dotty/annotation/internal/SourceFile.scala +++ b/src/scala/annotation/internal/SourceFile.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/dotty/annotation/internal/UnsafeNonvariant.scala b/src/scala/annotation/internal/UnsafeNonvariant.scala index 43a0a114b..b33df65d6 100644 --- a/src/dotty/annotation/internal/UnsafeNonvariant.scala +++ b/src/scala/annotation/internal/UnsafeNonvariant.scala @@ -1,4 +1,4 @@ -package dotty.annotation.internal +package scala.annotation.internal import scala.annotation.Annotation diff --git a/src/scalaShadowing/language.scala b/src/scalaShadowing/language.scala new file mode 100644 index 000000000..a74c9c671 --- /dev/null +++ b/src/scalaShadowing/language.scala @@ -0,0 +1,198 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2015, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scalaShadowing + +/** + * The `scala.language` object controls the language features available to the programmer, as proposed in the + * [[https://docs.google.com/document/d/1nlkvpoIRkx7at1qJEZafJwthZ3GeIklTFhqmXMvTX9Q/edit '''SIP-18 document''']]. + * + * Each of these features has to be explicitly imported into the current scope to become available: + * {{{ + * import language.postfixOps // or language._ + * List(1, 2, 3) reverse + * }}} + * + * The language features are: + * - [[dynamics `dynamics`]] enables defining calls rewriting using the [[scala.Dynamic `Dynamic`]] trait + * - [[postfixOps `postfixOps`]] enables postfix operators + * - [[reflectiveCalls `reflectiveCalls`]] enables using structural types + * - [[implicitConversions `implicitConversions`]] enables defining implicit methods and members + * - [[higherKinds `higherKinds`]] enables writing higher-kinded types + * - [[existentials `existentials`]] enables writing existential types + * - [[experimental `experimental`]] contains newer features that have not yet been tested in production + * + * and, for dotty: + * + * - [[Scala2 `Scala2`] backwards compatibility mode for Scala2 + * - [[noAtoTupling `noAutoTupling`]] disable auto-tupling + * + * @groupname production Language Features + * @groupname experimental Experimental Language Features + * @groupprio experimental 10 + * + * Dotty-specific features come at the end. + * + * Note: Due to the more restricted language import mechanism in dotty (only + * imports count, implicits are disregarded) we don't need the constructions + * of the inherited language features. A simple object for each feature is + * sufficient. + */ +object language { + + import languageFeature._ + + /** Where enabled, direct or indirect subclasses of trait scala.Dynamic can + * be defined. Unless dynamics is enabled, a definition of a class, trait, + * or object that has Dynamic as a base trait is rejected. Dynamic member + * selection of existing subclasses of trait Dynamic are unaffected; + * they can be used anywhere. + * + * '''Why introduce the feature?''' To enable flexible DSLs and convenient interfacing + * with dynamic languages. + * + * '''Why control it?''' Dynamic member selection can undermine static checkability + * of programs. Furthermore, dynamic member selection often relies on reflection, + * which is not available on all platforms. + * + * @group production + */ + @volatile implicit lazy val dynamics: dynamics = languageFeature.dynamics + + /** Only where enabled, postfix operator notation `(expr op)` will be allowed. + * + * '''Why keep the feature?''' Several DSLs written in Scala need the notation. + * + * '''Why control it?''' Postfix operators interact poorly with semicolon inference. + * Most programmers avoid them for this reason. + * + * @group production + */ + @volatile implicit lazy val postfixOps: postfixOps = languageFeature.postfixOps + + /** Only where enabled, accesses to members of structural types that need + * reflection are supported. Reminder: A structural type is a type of the form + * `Parents { Decls }` where `Decls` contains declarations of new members that do + * not override any member in `Parents`. To access one of these members, a + * reflective call is needed. + * + * '''Why keep the feature?''' Structural types provide great flexibility because + * they avoid the need to define inheritance hierarchies a priori. Besides, + * their definition falls out quite naturally from Scala’s concept of type refinement. + * + * '''Why control it?''' Reflection is not available on all platforms. Popular tools + * such as ProGuard have problems dealing with it. Even where reflection is available, + * reflective dispatch can lead to surprising performance degradations. + * + * @group production + */ + @volatile implicit lazy val reflectiveCalls: reflectiveCalls = languageFeature.reflectiveCalls + + /** Only where enabled, definitions of implicit conversions are allowed. An + * implicit conversion is an implicit value of unary function type `A => B`, + * or an implicit method that has in its first parameter section a single, + * non-implicit parameter. Examples: + * + * {{{ + * implicit def stringToInt(s: String): Int = s.length + * implicit val conv = (s: String) => s.length + * implicit def listToX(xs: List[T])(implicit f: T => X): X = ... + * }}} + * + * implicit values of other types are not affected, and neither are implicit + * classes. + * + * '''Why keep the feature?''' Implicit conversions are central to many aspects + * of Scala’s core libraries. + * + * '''Why control it?''' Implicit conversions are known to cause many pitfalls + * if over-used. And there is a tendency to over-use them because they look + * very powerful and their effects seem to be easy to understand. Also, in + * most situations using implicit parameters leads to a better design than + * implicit conversions. + * + * @group production + */ + @volatile implicit lazy val implicitConversions: implicitConversions = languageFeature.implicitConversions + + /** Only where this flag is enabled, higher-kinded types can be written. + * + * '''Why keep the feature?''' Higher-kinded types enable the definition of very general + * abstractions such as functor, monad, or arrow. A significant set of advanced + * libraries relies on them. Higher-kinded types are also at the core of the + * scala-virtualized effort to produce high-performance parallel DSLs through staging. + * + * '''Why control it?''' Higher kinded types in Scala lead to a Turing-complete + * type system, where compiler termination is no longer guaranteed. They tend + * to be useful mostly for type-level computation and for highly generic design + * patterns. The level of abstraction implied by these design patterns is often + * a barrier to understanding for newcomers to a Scala codebase. Some syntactic + * aspects of higher-kinded types are hard to understand for the uninitiated and + * type inference is less effective for them than for normal types. Because we are + * not completely happy with them yet, it is possible that some aspects of + * higher-kinded types will change in future versions of Scala. So an explicit + * enabling also serves as a warning that code involving higher-kinded types + * might have to be slightly revised in the future. + * + * @group production + */ + @volatile implicit lazy val higherKinds: higherKinds = languageFeature.higherKinds + + /** Only where enabled, existential types that cannot be expressed as wildcard + * types can be written and are allowed in inferred types of values or return + * types of methods. Existential types with wildcard type syntax such as `List[_]`, + * or `Map[String, _]` are not affected. + * + * '''Why keep the feature?''' Existential types are needed to make sense of Java’s wildcard + * types and raw types and the erased types of run-time values. + * + * '''Why control it?''' Having complex existential types in a code base usually makes + * application code very brittle, with a tendency to produce type errors with + * obscure error messages. Therefore, going overboard with existential types + * is generally perceived not to be a good idea. Also, complicated existential types + * might be no longer supported in a future simplification of the language. + * + * @group production + */ + @volatile implicit lazy val existentials: existentials = languageFeature.existentials + + /** The experimental object contains features that have been recently added but have not + * been thoroughly tested in production yet. + * + * Experimental features '''may undergo API changes''' in future releases, so production + * code should not rely on them. + * + * Programmers are encouraged to try out experimental features and + * [[http://issues.scala-lang.org report any bugs or API inconsistencies]] + * they encounter so they can be improved in future releases. + * + * @group experimental + */ + object experimental { + + import languageFeature.experimental._ + + /** Where enabled, macro definitions are allowed. Macro implementations and + * macro applications are unaffected; they can be used anywhere. + * + * '''Why introduce the feature?''' Macros promise to make the language more regular, + * replacing ad-hoc language constructs with a general powerful abstraction + * capability that can express them. Macros are also a more disciplined and + * powerful replacement for compiler plugins. + * + * '''Why control it?''' For their very power, macros can lead to code that is hard + * to debug and understand. + */ + @volatile implicit lazy val macros: macros = languageFeature.experimental.macros + } + + /** Where imported, a backwards compatibility mode for Scala2 is enabled */ + object Scala2 + + /** Where imported, auto-tupling is disabled */ + object noAutoTupling +} diff --git a/test/test/ModifiersParsingTest.scala b/test/test/ModifiersParsingTest.scala new file mode 100644 index 000000000..82aa33281 --- /dev/null +++ b/test/test/ModifiersParsingTest.scala @@ -0,0 +1,162 @@ +package test + +import org.junit.Test +import org.junit.Assert._ + +import dotty.tools.dotc.ast.untpd.modsDeco +import dotty.tools.dotc.ast.untpd._ +import dotty.tools.dotc.ast.{ Trees => d } +import dotty.tools.dotc.parsing.Parsers.Parser +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.core.Contexts.ContextBase +import dotty.tools.dotc.core.Flags + +object ModifiersParsingTest { + implicit val ctx = (new ContextBase).initialCtx + + implicit def parse(code: String): Tree = { + val (_, stats) = new Parser(new SourceFile("<meta>", code.toCharArray)).templateStatSeq() + stats match { case List(stat) => stat; case stats => Thicket(stats) } + } + + implicit class TreeDeco(val code: Tree) extends AnyVal { + def firstConstrValDef: ValDef = code match { + case d.TypeDef(_, d.Template(constr, _, _, _)) => + constr.vparamss.head.head + } + + def firstTypeParam: TypeDef = code match { + case d.TypeDef(_, d.Template(constr, _, _, _)) => + constr.tparams.head + } + + def defParam(i: Int): ValDef = code match { + case d.DefDef(_, _, vparamss, _, _) => + vparamss.head.toArray.apply(i) + } + + def defParam(i: Int, j: Int): ValDef = code match { + case d.DefDef(_, _, vparamss, _, _) => + vparamss.toArray.apply(i).toArray.apply(j) + } + + def funParam(i: Int): Tree = code match { + case Function(params, _) => + params.toArray.apply(i) + } + + def field(i: Int): Tree = code match { + case d.TypeDef(_, t: Template) => + t.body.toArray.apply(i) + } + + def field(name: String): Tree = code match { + case d.TypeDef(_, t: Template) => + t.body.find({ + case m: MemberDef => m.name.show == name + case _ => false + }).get + } + + def stat(i: Int): Tree = code match { + case d.Block(stats, expr) => + if (i < stats.length) stats.toArray.apply(i) + else expr + } + + def modifiers: List[Mod] = code match { + case t: MemberDef => t.mods.mods + } + } +} + + +class ModifiersParsingTest { + import ModifiersParsingTest._ + + + @Test def valDef = { + var source: Tree = "class A(var a: Int)" + assert(source.firstConstrValDef.modifiers == List(Mod.Var())) + + source = "class A(val a: Int)" + assert(source.firstConstrValDef.modifiers == List(Mod.Val())) + + source = "class A(private val a: Int)" + assert(source.firstConstrValDef.modifiers == List(Mod.Private(), Mod.Val())) + + source = "class A(protected var a: Int)" + assert(source.firstConstrValDef.modifiers == List(Mod.Protected(), Mod.Var())) + + source = "class A(protected implicit val a: Int)" + assert(source.firstConstrValDef.modifiers == List(Mod.Protected(), Mod.Implicit(), Mod.Val())) + + source = "class A[T]" + assert(source.firstTypeParam.modifiers == List()) + + source = "class A[type T]" + assert(source.firstTypeParam.modifiers == List(Mod.Type())) + } + + @Test def typeDef = { + var source: Tree = "class A" + assert(source.modifiers == List()) + + source = "sealed class A" + assert(source.modifiers == List(Mod.Sealed())) + + source = "implicit class A" + assert(source.modifiers == List(Mod.Implicit())) + + source = "abstract sealed class A" + assert(source.modifiers == List(Mod.Abstract(), Mod.Sealed())) + } + + @Test def fieldDef = { + val source: Tree = + """ + | class A { + | lazy var a = ??? + | lazy private val b = ??? + | final val c = ??? + | + | abstract override def f: Boolean + | inline def g(n: Int) = ??? + | } + """.stripMargin + + assert(source.field("a").modifiers == List(Mod.Lazy(), Mod.Var())) + assert(source.field("b").modifiers == List(Mod.Lazy(), Mod.Private(), Mod.Val())) + assert(source.field("c").modifiers == List(Mod.Final(), Mod.Val())) + assert(source.field("f").modifiers == List(Mod.Abstract(), Mod.Override())) + assert(source.field("g").modifiers == List(Mod.Inline())) + } + + @Test def paramDef = { + var source: Tree = "def f(inline a: Int) = ???" + assert(source.defParam(0).modifiers == List(Mod.Inline())) + + 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))) + + 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))) + } + + @Test def blockDef = { + var source: Tree = "implicit val x : A = ???" + assert(source.modifiers == List(Mod.Implicit(), Mod.Val())) + + source = "implicit var x : A = ???" + assert(source.modifiers == List(Mod.Implicit(), Mod.Var())) + + source = "{ implicit var x : A = ??? }" + assert(source.stat(0).modifiers == List(Mod.Implicit(), Mod.Var())) + + source = "{ implicit x => x * x }" + assert(source.stat(0).funParam(0).modifiers == List(Mod.Implicit())) + } +} diff --git a/tests/neg/i1286.scala b/tests/neg/i1286.scala new file mode 100644 index 000000000..40db9ab1d --- /dev/null +++ b/tests/neg/i1286.scala @@ -0,0 +1,16 @@ +import scala.idontexist // error +import scala.io.Idontexist // error + +import scala.io +import io.Idontexist2 // error + +import scala.io.{ AnsiColor, Idontexist3 } // error + +import scala.io.{ Idontexist4 => Foo } // error +import scala.io.{ Idontexist5 => _ } // error + +import scala.language.dynamics +import scala.language.noAutoTupling +import scala.language.idontexist // error + +object Test diff --git a/tests/neg/i1531.scala b/tests/neg/i1531.scala new file mode 100644 index 000000000..3c05e9509 --- /dev/null +++ b/tests/neg/i1531.scala @@ -0,0 +1,6 @@ +trait T { + def f: Int +} + +class A(f: Int) extends T // error: class A needs to be abstract + diff --git a/tests/pos/1567/PosZInt_1.scala b/tests/pos/1567/PosZInt_1.scala new file mode 100644 index 000000000..60b4061e6 --- /dev/null +++ b/tests/pos/1567/PosZInt_1.scala @@ -0,0 +1,6 @@ +final class PosZInt private (val value: Int) extends AnyVal + +object PosZInt { + def from(value: Int): Option[PosZInt] = + if (value >= 0) Some(new PosZInt(value)) else None +} diff --git a/tests/pos/1567/Test_2.scala b/tests/pos/1567/Test_2.scala new file mode 100644 index 000000000..db6ca7070 --- /dev/null +++ b/tests/pos/1567/Test_2.scala @@ -0,0 +1,3 @@ +object Test { + scala.util.Try(PosZInt.from(1).get) +} diff --git a/tests/pos/i1500.scala b/tests/pos/i1500.scala new file mode 100644 index 000000000..713d06689 --- /dev/null +++ b/tests/pos/i1500.scala @@ -0,0 +1,19 @@ +sealed trait HList +sealed trait HNil extends HList +sealed trait ::[+H, +T <: HList] extends HList + +case class Size[L <: HList](value: Int) + +object Size { + implicit val caseHNil: Size[HNil] = Size(0) + implicit def caseHCons[H, T <: HList](implicit e: Size[T]): Size[H :: T] = Size(e.value + 1) +} + +object HListTest { + def main(args: Array[String]): Unit = { + assert(implicitly[Size[HNil]].value == 0) + assert(implicitly[Size[Int :: HNil]].value == 1) + assert(implicitly[Size[Int :: Int :: HNil]].value == 2) + assert(implicitly[Size[Int :: Int :: Int :: HNil]].value == 3) + } +} diff --git a/tests/pos/i1515.scala b/tests/pos/i1515.scala new file mode 100644 index 000000000..fb3ad78ee --- /dev/null +++ b/tests/pos/i1515.scala @@ -0,0 +1,16 @@ +sealed trait Trait[T] + +final case class Case[T](e: T) extends Trait[T] + +object Demo { + def main(args: Array[String]): Unit = { + + def f[H](t: Trait[H]): Unit = + t match { + case Case(e) => println(Some(e)) + } + + f(Case(1)) + + } +} diff --git a/tests/pos/i1540.scala b/tests/pos/i1540.scala new file mode 100644 index 000000000..7aa24f459 --- /dev/null +++ b/tests/pos/i1540.scala @@ -0,0 +1,14 @@ +class Casey1(val a: Int) { + def isDefined: Boolean = true + def isDefined(x: Int): Boolean = ??? + def get: Int = a + def get(x: Int): String = ??? +} +object Casey1 { def unapply(a: Casey1) = a } + +object Test { + def main(args: Array[String]): Unit = { + val c @ Casey1(x) = new Casey1(0) + assert(x == c.get) + } +} diff --git a/tests/pos/i1540b.scala b/tests/pos/i1540b.scala new file mode 100644 index 000000000..2b4c5408e --- /dev/null +++ b/tests/pos/i1540b.scala @@ -0,0 +1,14 @@ +class Casey1[T](val a: T) { + def isDefined: Boolean = true + def isDefined(x: T): Boolean = ??? + def get: T = a + def get(x: T): String = ??? +} +object Casey1 { def unapply[T](a: Casey1[T]) = a } + +object Test { + def main(args: Array[String]): Unit = { + val c @ Casey1(x) = new Casey1(0) + assert(x == c.get) + } +} diff --git a/tests/pos/i1544.scala b/tests/pos/i1544.scala new file mode 100644 index 000000000..f98d346e8 --- /dev/null +++ b/tests/pos/i1544.scala @@ -0,0 +1,4 @@ +object Foo { + def foo(p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int, p8: Int, p9: Int, p10: Int, p11: Int, p12: Int, p13: Int, p14: Int, p15: Int, p16: Int, p17: Int, p18: Int, p19: Int, p20: Int, p21: Int, p22: Int, p23: Int, p24: Int, p25: Int, p26: Int, p27: Int, p28: Int, p29: Int, p30: Int, p31: Int, p32: Int, p33: Int, p34: Int, p35: Int, p36: Int, p37: Int, p38: Int, p39: Int, p40: Int, p41: Int, p42: Int, p43: Int, p44: Int, p45: Int, p46: Int, p47: Int, p48: Int, p49: Int, p50: Int, p51: Int, p52: Int, p53: Int, p54: Int, p55: Int, p56: Int, p57: Int, p58: Int, p59: Int, p60: Int, p61: Int, p62: Int, p63: Int, p64: Int, p65: Int, p66: Int, p67: Int, p68: Int, p69: Int, p70: Int, p71: Int, p72: Int, p73: Int, p74: Int, p75: Int, p76: Int, p77: Int, p78: Int, p79: Int, p80: Int, p81: Int, p82: Int, p83: Int, p84: Int, p85: Int, p86: Int, p87: Int, p88: Int, p89: Int, p90: Int, p91: Int, p92: Int, p93: Int, p94: Int, p95: Int, p96: Int, p97: Int, p98: Int, p99: Int, p100: Int, p101: Int, p102: Int, p103: Int, p104: Int, p105: Int, p106: Int, p107: Int, p108: Int, p109: Int): Int = 42 + foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109) +} diff --git a/tests/pos/i1570.scala b/tests/pos/i1570.scala new file mode 100644 index 000000000..c2e4fd01b --- /dev/null +++ b/tests/pos/i1570.scala @@ -0,0 +1,4 @@ +object Test { + inline def foo(inline n: Int) = bar(n) + inline def bar(inline n: Int) = n +} diff --git a/tests/pos/i1590.scala b/tests/pos/i1590.scala new file mode 100644 index 000000000..ab922c218 --- /dev/null +++ b/tests/pos/i1590.scala @@ -0,0 +1,10 @@ +case class W[T](seq: Option[Option[T]] = Option.empty) +object W { + def apply[T] = new W[T]() +} + +case class V[T](vv: W[W[T]] = W.apply) +object Test { + W[Int]() + // V[Int]() fails in scalac and dotty: both instantiate the vv-default to W[Nothing] +} diff --git a/tests/pos/java-interop/1576/TagAnnotation.java b/tests/pos/java-interop/1576/TagAnnotation.java new file mode 100644 index 000000000..3034a1a2d --- /dev/null +++ b/tests/pos/java-interop/1576/TagAnnotation.java @@ -0,0 +1,3 @@ +public @interface TagAnnotation { + public String value(); +}
\ No newline at end of file diff --git a/tests/pos/java-interop/1576/Test.scala b/tests/pos/java-interop/1576/Test.scala new file mode 100644 index 000000000..ea3d06a83 --- /dev/null +++ b/tests/pos/java-interop/1576/Test.scala @@ -0,0 +1,5 @@ +object Test { + val v: TagAnnotation = null + println(v.value) // error: value value in class TagAnnotation cannot be accessed as a + // member of TagAnnotation(Test.v) from module class Test$. +} diff --git a/tests/pos/tailcall/i1614.scala b/tests/pos/tailcall/i1614.scala new file mode 100644 index 000000000..4c10e963d --- /dev/null +++ b/tests/pos/tailcall/i1614.scala @@ -0,0 +1,9 @@ +object Foobar { + def apply(): Option[String] = { + def foobar[A](f: (String, String) => A): List[A] = List[String]() match { + case _ :: ls => foobar(f) + } + foobar((a1, a2) => (a1 + a2)).headOption + } +} + diff --git a/tests/repl/imports.check b/tests/repl/imports.check index 26b725637..b6d9ae8a7 100644 --- a/tests/repl/imports.check +++ b/tests/repl/imports.check @@ -15,4 +15,14 @@ scala> buf += xs | scala> buf ++= xs res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) +scala> import util.foo +-- Error: <console> ---------------------------------------------------------------------------------------------------- +8 |import util.foo + | ^^^ + | foo is not a member of util +scala> import util.foo.bar +-- [E008] Member Not Found Error: <console> ---------------------------------------------------------------------------- +8 |import util.foo.bar + | ^^^^^^^^ + | value `foo` is not a member of util.type - did you mean `util.Left`? scala> :quit |