diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b8c17ff61e9e..2ee750acc84f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1513,16 +1513,29 @@ object SymDenotations { * See tests/pos/i10769.scala */ def reachableTypeRef(using Context) = - TypeRef(owner.reachableThisType, symbol) + TypeRef(owner.reachablePrefix, symbol) - /** Like termRef, but objects in the prefix are represented by their singleton type, + /** The reachable typeRef with wildcard arguments for each type parameter */ + def reachableRawTypeRef(using Context) = + reachableTypeRef.appliedTo(typeParams.map(_ => TypeBounds.emptyPolyKind)) + + /** Like termRef, if it is addressable from the current context, + * but objects in the prefix are represented by their singleton type, * this means we output `pre.O.member` rather than `pre.O$.this.member`. * * This is required to avoid owner crash in ExplicitOuter. * See tests/pos/i10769.scala + * + * If the reference is to an object that is not accessible from the + * current context since the object is nested in a class that is not an outer + * class of the current context, fall back to a TypeRef to the module class. + * Test case is tests/pos/i17556.scala. + * If the reference is to some other inaccessible object, throw an AssertionError. */ - def reachableTermRef(using Context) = - TermRef(owner.reachableThisType, symbol) + def reachableTermRef(using Context): Type = owner.reachablePrefix match + case pre: SingletonType => TermRef(pre, symbol) + case pre if symbol.is(ModuleVal) => TypeRef(pre, symbol.moduleClass) + case _ => throw AssertionError(i"cannot compute path to TermRef $this from ${ctx.owner}") /** Like thisType, but objects in the type are represented by their singleton type, * this means we output `pre.O.member` rather than `pre.O$.this.member`. @@ -1537,6 +1550,18 @@ object SymDenotations { else ThisType.raw(TypeRef(owner.reachableThisType, symbol.asType)) + /** Like `reachableThisType`, except if that would refer to a class where + * the `this` cannot be accessed. In that case, fall back to the + * rawTypeRef of the class. E.g. instead of `A.this.X` where `A.this` + * is inaccessible, use `A#X`. + */ + def reachablePrefix(using Context): Type = reachableThisType match + case pre: ThisType + if !pre.cls.isStaticOwner && !ctx.owner.isContainedIn(pre.cls) => + pre.cls.reachableRawTypeRef + case pre => + pre + /** The variance of this type parameter or type member as a subset of * {Covariant, Contravariant} */ diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7282905f559f..54f8ca02eb4a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3037,7 +3037,8 @@ object Types { abstract case class SuperType(thistpe: Type, supertpe: Type) extends CachedProxyType with SingletonType { override def underlying(using Context): Type = supertpe override def superType(using Context): Type = - thistpe.baseType(supertpe.typeSymbol) + if supertpe.typeSymbol.exists then thistpe.baseType(supertpe.typeSymbol) + else super.superType def derivedSuperType(thistpe: Type, supertpe: Type)(using Context): Type = if ((thistpe eq this.thistpe) && (supertpe eq this.supertpe)) this else SuperType(thistpe, supertpe) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 0c701eb03d38..71e00f985584 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -991,7 +991,9 @@ class ClassfileParser( return unpickleTASTY(tastyBytes) } } - else return unpickleTASTY(bytes) + else + // Before 3.0.0 we had a mode where we could embed the TASTY bytes in the classfile. This has not been supported in any stable release. + report.error(s"Found a TASTY attribute with a length different from 16 in $classfile. This is likely a bug in the compiler. Please report.", NoSourcePosition) } if scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7a29ac3f7a38..3079b26df6cd 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1468,8 +1468,8 @@ object Parsers { * PolyFunType ::= HKTypeParamClause '=>' Type * | HKTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions * FunTypeArgs ::= InfixType - * | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)' - * | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' + * | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)' + * | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' */ def typ(): Tree = val start = in.offset diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 15a1a823589c..8b58f18bca52 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -17,13 +17,13 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.config.ScalaRelease.* -import scala.collection.mutable import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import scala.annotation.constructorOnly +import scala.collection.mutable /** Translates quoted terms and types to `unpickleExprV2` or `unpickleType` method calls. * @@ -106,16 +106,19 @@ class PickleQuotes extends MacroTransform { private def extractHolesContents(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) = class HoleContentExtractor extends Transformer: private val holeContents = List.newBuilder[Tree] + private val stagedClasses = mutable.HashSet.empty[Symbol] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case tree @ Hole(isTerm, _, _, content) => assert(isTerm) assert(!content.isEmpty) holeContents += content - val holeType = getTermHoleType(tree.tpe) + val holeType = getPicklableHoleType(tree.tpe, stagedClasses) val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType) cpy.Inlined(tree)(EmptyTree, Nil, hole) case tree: DefTree => + if tree.symbol.isClass then + stagedClasses += tree.symbol val newAnnotations = tree.symbol.annotations.mapconserve { annot => annot.derivedAnnotation(transform(annot.tree)(using ctx.withOwner(tree.symbol))) } @@ -134,19 +137,6 @@ class PickleQuotes extends MacroTransform { } } - /** Remove references to local types that will not be defined in this quote */ - private def getTermHoleType(using Context) = new TypeMap() { - override def apply(tp: Type): Type = tp match - case tp @ TypeRef(NoPrefix, _) => - // reference to term with a type defined in outer quote - getTypeHoleType(tp) - case tp @ TermRef(NoPrefix, _) => - // widen term refs to terms defined in outer quote - apply(tp.widenTermRefExpr) - case tp => - mapOver(tp) - } - /** Get the holeContents of the transformed tree */ def getContents() = val res = holeContents.result @@ -196,11 +186,11 @@ class PickleQuotes extends MacroTransform { cpy.Quote(quote)(Block(tdefs, body1), quote.tags) private def mkTagSymbolAndAssignType(typeArg: Tree, idx: Int)(using Context): TypeDef = { - val holeType = getTypeHoleType(typeArg.tpe.select(tpnme.Underlying)) + val holeType = getPicklableHoleType(typeArg.tpe.select(tpnme.Underlying), _ => false) val hole = untpd.cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree).withType(holeType) val local = newSymbol( owner = ctx.owner, - name = UniqueName.fresh(hole.tpe.dealias.typeSymbol.name.toTypeName), + name = UniqueName.fresh(typeArg.symbol.name.toTypeName), flags = Synthetic, info = TypeAlias(typeArg.tpe.select(tpnme.Underlying)), coord = typeArg.span @@ -209,25 +199,11 @@ class PickleQuotes extends MacroTransform { ctx.typeAssigner.assignType(untpd.TypeDef(local.name, hole), local).withSpan(typeArg.span) } - /** Remove references to local types that will not be defined in this quote */ - private def getTypeHoleType(using Context) = new TypeMap() { - override def apply(tp: Type): Type = tp match - case tp: TypeRef if tp.typeSymbol.isTypeSplice => - apply(tp.dealias) - case tp @ TypeRef(pre, _) if isLocalPath(pre) => - val hiBound = tp.typeSymbol.info match - case info: ClassInfo => info.parents.reduce(_ & _) - case info => info.hiBound - apply(hiBound) - case tp => - mapOver(tp) - - private def isLocalPath(tp: Type): Boolean = tp match - case NoPrefix => true - case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix) - case tp => false - } - + /** Avoid all non-static types except those defined in the quote. */ + private def getPicklableHoleType(tpe: Type, isStagedClasses: Symbol => Boolean)(using Context) = + new TypeOps.AvoidMap { + def toAvoid(tp: NamedType) = !isStagedClasses(tp.typeSymbol) && !isStaticPrefix(tp) + }.apply(tpe) } object PickleQuotes { diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index c02a7d90cb8c..3fe05a45699e 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -333,10 +333,6 @@ object SymUtils: else owner.isLocal } - /** The reachable typeRef with wildcard arguments for each type parameter */ - def reachableRawTypeRef(using Context) = - self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) - /** Is symbol a type splice operation? */ def isTypeSplice(using Context): Boolean = self == defn.QuotedType_splice diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 48bcbaab3511..1560ae6e5618 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -552,7 +552,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { .map((pre, child) => rawRef(child).asSeenFrom(pre, child.owner)) case _ => cls.children.map(rawRef) - end computeChildTypes + val childTypes = computeChildTypes val cases = for (patType, idx) <- childTypes.zipWithIndex yield diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index cc4433f75a68..df708057dd71 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1122,7 +1122,10 @@ class Namer { typer: Typer => No("is already an extension method, cannot be exported into another one") else if targets.contains(alias) then No(i"clashes with another export in the same export clause") - else if sym.is(Override) then + else if sym.is(Override) || sym.is(JavaDefined) then + // The tests above are used to avoid futile searches of `allOverriddenSymbols`. + // Scala defined symbols can override concrete symbols only if declared override. + // For Java defined symbols, this does not hold, so we have to search anyway. sym.allOverriddenSymbols.find( other => cls.derivesFrom(other.owner) && !other.is(Deferred) ) match diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 9888916a86c9..81c0d3e35d3a 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -22,6 +22,7 @@ i15922.scala t5031_2.scala i16997.scala i7414.scala +i17588.scala # Tree is huge and blows stack for printing Text i7034.scala diff --git a/docs/_docs/reference/changed-features/type-checking.md b/docs/_docs/reference/changed-features/type-checking.md deleted file mode 100644 index 6f59b1a1c1c6..000000000000 --- a/docs/_docs/reference/changed-features/type-checking.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: doc-page -title: "Changes in Type Checking" -nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/type-checking.html ---- - -*** **TO BE FILLED IN** *** diff --git a/docs/_docs/reference/dropped-features/nonlocal-returns.md b/docs/_docs/reference/dropped-features/nonlocal-returns.md index 17b86f77ee56..e6490b7ca5bc 100644 --- a/docs/_docs/reference/dropped-features/nonlocal-returns.md +++ b/docs/_docs/reference/dropped-features/nonlocal-returns.md @@ -9,21 +9,17 @@ Returning from nested anonymous functions has been deprecated, and will produce Nonlocal returns are implemented by throwing and catching `scala.runtime.NonLocalReturnException`-s. This is rarely what is intended by the programmer. It can be problematic because of the hidden performance cost of throwing and catching exceptions. Furthermore, it is a leaky implementation: a catch-all exception handler can intercept a `NonLocalReturnException`. -A drop-in library replacement is provided in [`scala.util.control.NonLocalReturns`](https://scala-lang.org/api/3.x/scala/util/control/NonLocalReturns$.html). Example: +A better alternative to nonlocal returns and also the `scala.util.control.Breaks` API is provided by [`scala.util.boundary` and `boundary.break`](http://dotty.epfl.ch/api/scala/util/boundary$.html). -```scala -import scala.util.control.NonLocalReturns.* - -extension [T](xs: List[T]) - def has(elem: T): Boolean = returning { - for x <- xs do - if x == elem then throwReturn(true) - false - } +Example: -@main def test(): Unit = - val xs = List(1, 2, 3, 4, 5) - assert(xs.has(2) == xs.contains(2)) +```scala +import scala.util.boundary, boundary.break +def firstIndex[T](xs: List[T], elem: T): Int = + boundary: + for (x, i) <- xs.zipWithIndex do + if x == elem then break(i) + -1 ``` Note: compiler produces deprecation error on nonlocal returns only with `-source:future` option. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 345134cf2e9b..30ad05d18cf1 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -92,7 +92,6 @@ subsection: - page: reference/changed-features/operators.md - page: reference/changed-features/wildcards.md - page: reference/changed-features/imports.md - - page: reference/changed-features/type-checking.md - page: reference/changed-features/type-inference.md - page: reference/changed-features/implicit-resolution.md - page: reference/changed-features/implicit-conversions.md diff --git a/project/resources/referenceReplacements/sidebar.yml b/project/resources/referenceReplacements/sidebar.yml index be67a0d6da99..de0f3d7bec2c 100644 --- a/project/resources/referenceReplacements/sidebar.yml +++ b/project/resources/referenceReplacements/sidebar.yml @@ -88,7 +88,6 @@ subsection: - page: reference/changed-features/operators.md - page: reference/changed-features/wildcards.md - page: reference/changed-features/imports.md - - page: reference/changed-features/type-checking.md - page: reference/changed-features/type-inference.md - page: reference/changed-features/implicit-resolution.md - page: reference/changed-features/implicit-conversions.md diff --git a/project/scripts/expected-links/reference-expected-links.txt b/project/scripts/expected-links/reference-expected-links.txt index a09737574c1d..0fbb84831e37 100644 --- a/project/scripts/expected-links/reference-expected-links.txt +++ b/project/scripts/expected-links/reference-expected-links.txt @@ -18,7 +18,6 @@ ./changed-features/pattern-matching.html ./changed-features/structural-types-spec.html ./changed-features/structural-types.html -./changed-features/type-checking.html ./changed-features/type-inference.html ./changed-features/vararg-splices.html ./changed-features/wildcards.html diff --git a/tests/pos/i17556.scala b/tests/pos/i17556.scala new file mode 100644 index 000000000000..9f14cbfbb7c1 --- /dev/null +++ b/tests/pos/i17556.scala @@ -0,0 +1,8 @@ +sealed trait A { + // must be `object` or `case class` + object X extends A + case class Y() extends A +} + +// companion object must exist +object A \ No newline at end of file diff --git a/tests/pos/i17588.scala b/tests/pos/i17588.scala new file mode 100644 index 000000000000..5ac63d0dcc05 --- /dev/null +++ b/tests/pos/i17588.scala @@ -0,0 +1,2 @@ +class StringBox(inner: String): + export inner.* \ No newline at end of file diff --git a/tests/run/i15913.scala b/tests/run/i15913.scala new file mode 100644 index 000000000000..f3e98a3bfd6a --- /dev/null +++ b/tests/run/i15913.scala @@ -0,0 +1,20 @@ +// https://github.com/lampepfl/dotty/issues/15913 + +class injector[F] + +object injectorFactory { + def apply[F](overrides: String*): injector[F] = new injector[F] + + def apply[F]( + bootstrapActivation: Int = ???, + overrides: Seq[String] = Seq.empty, + ): injector[F] = new injector[F] +} + +object Test extends App { + println( + injectorFactory[String]( + bootstrapActivation = 0 + ) + ) +} diff --git a/tests/run/i17555.scala b/tests/run/i17555.scala new file mode 100644 index 000000000000..6458cb7f7efb --- /dev/null +++ b/tests/run/i17555.scala @@ -0,0 +1,17 @@ +class Root { + override def toString() = "Root" +} +trait A extends Root with B { } +trait B { + override def toString() = "B" +} +case class C() extends A { + override def toString() = super.toString() +} +class D() extends A, Serializable { + override def toString() = super.toString() +} + +@main def Test = + assert(C().toString == "B") + assert(D().toString == "B")