diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index c586bb06cc29..b8cbb4367db4 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -117,7 +117,7 @@ object BetaReduce: case ref @ TypeRef(NoPrefix, _) => ref.symbol case _ => - val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, targ.tpe, coord = targ.span)).withSpan(targ.span) + val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, TypeAlias(targ.tpe), coord = targ.span)).withSpan(targ.span) bindings += binding binding.symbol diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 52942ea719f9..96d6da39a913 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -533,6 +533,11 @@ object TreeChecker { i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") } + override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = { + assert(sym.info.isInstanceOf[ClassInfo | TypeBounds], i"wrong type, expect a template or type bounds for ${sym.fullName}, but found: ${sym.info}") + super.typedTypeDef(tdef, sym) + } + override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(using Context): Tree = { val TypeDef(_, impl @ Template(constr, _, _, _)) = cdef: @unchecked assert(cdef.symbol == cls) diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index e934c1930163..a6a773adc9ba 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1345,18 +1345,22 @@ object SourceCode { } private def printBoundsTree(bounds: TypeBoundsTree)(using elideThis: Option[Symbol]): this.type = { - bounds.low match { - case Inferred() => - case low => - this += " >: " - printTypeTree(low) - } - bounds.hi match { - case Inferred() => this - case hi => - this += " <: " - printTypeTree(hi) - } + if bounds.low.tpe == bounds.hi.tpe then + this += " = " + printTypeTree(bounds.low) + else + bounds.low match { + case Inferred() => + case low => + this += " >: " + printTypeTree(low) + } + bounds.hi match { + case Inferred() => this + case hi => + this += " <: " + printTypeTree(hi) + } } private def printBounds(bounds: TypeBounds)(using elideThis: Option[Symbol]): this.type = { diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala index ba59f77495b1..3c34a1c9bba9 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala @@ -7,10 +7,66 @@ import scala.util.matching.Regex object Scaladoc2AnchorCreator: - def getScaladoc2Type(using Quotes)(t: reflect.Tree) = - import reflect.* - val regex = t match - case d: DefDef => "def" - case t: TypeDef => "type" - case v: ValDef => "val|var" - t.show(using Printer.TreeShortCode).split(regex, 2)(1).replace(" ","") + def getScaladoc2Type(using Quotes)(sym: quotes.reflect.Symbol) = signatureAnchor(sym) + + /** Creates the signature anchor + * + * - `X` for a `type X ...` + * - `x:X` for a `val x: X` + * - `f[U1,...](x1:T1,...)(impliciti1:U1,impliciti2:U2,...)...:R` for a `def f[U1, ...](x1: T1, ...)(implicit i1: U1, i2: U2...)...: R` + * + * Types are printed without their paths. No spaces are printed in the output. + */ + private def signatureAnchor(using Quotes)(sym: quotes.reflect.Symbol): String = + import quotes.reflect.* + def signatureType(tp: quotes.reflect.TypeRepr): String = + tp match + case mt @ MethodType(paramNames, paramTypes, res) => + val implicitPrefix = if mt.isImplicit then "implicit" else "" + val closeClause = res match + case _: MethodOrPoly => ")" + case _ => "):" + paramNames.zip(paramTypes.map(signatureType)) + .map((name, tpe) => s"$implicitPrefix$name:$tpe") + .mkString("(", ",", closeClause) + signatureType(res) + case PolyType(paramNames, paramBounds, res) => + val closeClause = res match + case _: MethodOrPoly => "]" + case _ => "]:" + paramNames.zip(paramBounds.map(signatureType)) + .map((name, tpe) => s"$name$tpe") + .mkString("[", ",", closeClause) + signatureType(res) + case TypeLambda(paramNames, paramBounds, res) => + paramNames.zip(paramBounds.map(signatureType)) + .map((name, tpe) => s"$name$tpe") + .mkString("[", ",", "]") + "=>" + signatureType(res) + case ByNameType(tp) => + ":" + signatureType(tp) + case TypeBounds(low, hi) => + val lowBound = if low =:= defn.NothingClass.typeRef then "" else ">:" + signatureType(low) + val hiBound = if low =:= defn.AnyClass.typeRef then "" else "<:" + signatureType(hi) + lowBound + hiBound + case tp: ParamRef => + tp.binder match + case binder: MethodType => binder.paramNames(tp.paramNum) + ".type" + case binder: PolyType => binder.paramNames(tp.paramNum) + case binder: LambdaType => binder.paramNames(tp.paramNum) + case AppliedType(tycon, args) => + args.map { + case tp: TypeBounds => "_" + signatureType(tp) + case tp => signatureType(tp) + }.mkString(signatureType(tycon) + "[", ",", "]") + case tp: AnnotatedType => + signatureType(tp.underlying) + "@" + tp.annotation.symbol.owner.name + case tp: ThisType => + signatureType(tp.tref) + ".this" + case tp: TypeRef => + tp.name + case tp => + // TODO handle other cases without using show (show does not have a stable representation) + tp.show(using Printer.TypeReprShortCode).replace(" ","") + + sym match + case sym if sym.isType => sym.name + case sym if sym.flags.is(Flags.Method) => sym.name + signatureType(sym.info) + case sym => sym.name + ":" + signatureType(sym.info) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index 584ef1f54267..5bc1b98a7fff 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -235,7 +235,7 @@ class SymOpsWithLinkCache: def constructPathForScaladoc2: String = val l = escapeUrl(location.mkString("/")) val scaladoc2Anchor = if anchor.isDefined then { - "#" + getScaladoc2Type(sym.tree) + "#" + getScaladoc2Type(sym) } else "" docURL + l + extension + scaladoc2Anchor diff --git a/tests/pos/i17052.scala b/tests/pos/i17052.scala new file mode 100644 index 000000000000..4bb8cf493d0b --- /dev/null +++ b/tests/pos/i17052.scala @@ -0,0 +1,2 @@ +def test[F[_]](fAny: F[Any]) = + { [X] => (fx: F[X]) => { val fx2: F[X] = fx; () } }.apply[Any](fAny)