Skip to content

Commit 348e9db

Browse files
authored
Merge pull request #9095 from dotty-staging/throws-annot-3
2 parents 7349d7f + a3f6671 commit 348e9db

26 files changed

+298
-136
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import scala.collection.mutable
1111
import dotty.tools.dotc.CompilationUnit
1212
import dotty.tools.dotc.ast.tpd
1313
import dotty.tools.dotc.ast.Trees
14-
import dotty.tools.dotc.core.Annotations.Annotation
14+
import dotty.tools.dotc.core.Annotations._
1515
import dotty.tools.dotc.core.Constants._
1616
import dotty.tools.dotc.core.Contexts.Context
1717
import dotty.tools.dotc.core.Decorators._
@@ -368,14 +368,12 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
368368
case StringTag =>
369369
assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant`
370370
av.visit(name, const.stringValue) // `stringValue` special-cases null, but that execution path isn't exercised for a const with StringTag
371-
case ClazzTag => av.visit(name, typeToTypeKind(const.typeValue)(bcodeStore)(innerClasesStore).toASMType)
371+
case ClazzTag => av.visit(name, typeToTypeKind(TypeErasure.erasure(const.typeValue))(bcodeStore)(innerClasesStore).toASMType)
372372
case EnumTag =>
373373
val edesc = innerClasesStore.typeDescriptor(const.tpe) // the class descriptor of the enumeration class.
374374
val evalue = const.symbolValue.javaSimpleName // value the actual enumeration value.
375375
av.visitEnum(name, edesc, evalue)
376376
}
377-
case t: TypeApply if (t.fun.symbol == defn.Predef_classOf) =>
378-
av.visit(name, typeToTypeKind(t.args.head.tpe.classSymbol.denot.info)(bcodeStore)(innerClasesStore).toASMType)
379377
case Ident(nme.WILDCARD) =>
380378
// An underscore argument indicates that we want to use the default value for this parameter, so do not emit anything
381379
case t: tpd.RefTree if t.symbol.denot.owner.isAllOf(JavaEnumTrait) =>
@@ -514,7 +512,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
514512

515513
// TODO needed? for(ann <- m.annotations) { ann.symbol.initialize }
516514
val jgensig = getStaticForwarderGenericSignature(m, module)
517-
val (throws, others) = m.annotations partition (_.tree.symbol == defn.ThrowsAnnot)
515+
val (throws, others) = m.annotations.partition(_.symbol eq defn.ThrowsAnnot)
518516
val thrownExceptions: List[String] = getExceptions(throws)
519517

520518
val jReturnType = toTypeKind(methodInfo.resultType)
@@ -614,12 +612,9 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
614612
* must-single-thread
615613
*/
616614
def getExceptions(excs: List[Annotation]): List[String] = {
617-
// TODO: implement ThrownException
618-
// for (ThrownException(exc) <- excs.distinct)
619-
// yield internalName(exc)
620-
Nil
615+
for (case ThrownException(exc) <- excs.distinct)
616+
yield internalName(TypeErasure.erasure(exc).classSymbol)
621617
}
622-
623618
} // end of trait BCForwardersGen
624619

625620
trait BCClassGen extends BCInnerClassGen {

compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
513513
def initJMethod(flags: Int, paramAnnotations: List[List[Annotation]]): Unit = {
514514

515515
val jgensig = getGenericSignature(methSymbol, claszSymbol)
516-
val (excs, others) = methSymbol.annotations partition (_.tree.symbol == defn.ThrowsAnnot)
516+
val (excs, others) = methSymbol.annotations.partition(_.symbol eq defn.ThrowsAnnot)
517517
val thrownExceptions: List[String] = getExceptions(excs)
518518

519519
val bytecodeName =

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ class Compiler {
6868
new CacheAliasImplicits, // Cache RHS of parameterless alias implicits
6969
new ByNameClosures, // Expand arguments to by-name parameters to closures
7070
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
71-
new ClassOf, // Expand `Predef.classOf` calls.
7271
new RefChecks) :: // Various checks mostly related to abstract members and overriding
7372
List(new ElimOpaque, // Turn opaque into normal aliases
7473
new TryCatchPatterns, // Compile cases in try/catch
@@ -109,8 +108,7 @@ class Compiler {
109108
List(new Constructors, // Collect initialization code in primary constructors
110109
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
111110
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
112-
new Instrumentation, // Count closure allocations under -Yinstrument-closures
113-
new GetClass) :: // Rewrites getClass calls on primitive types.
111+
new Instrumentation) :: // Count closure allocations under -Yinstrument-closures
114112
List(new LinkScala2Impls, // Redirect calls to trait methods defined by Scala 2.x, so that they now go to
115113
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
116114
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,19 @@ object desugar {
460460
case tree: MemberDef => Ident(tree.name.toTermName) :: Nil
461461
case PatDef(_, ids: List[Ident] @ unchecked, _, _) => ids
462462
}
463-
val stats = impl.body.map(expandConstructor)
463+
464+
val stats0 = impl.body.map(expandConstructor)
465+
val stats =
466+
if (ctx.owner eq defn.ScalaPackageClass) && defn.hasProblematicGetClass(className) then
467+
stats0.filterConserve {
468+
case ddef: DefDef =>
469+
ddef.name ne nme.getClass_
470+
case _ =>
471+
true
472+
}
473+
else
474+
stats0
475+
464476
if (isEnum) {
465477
val (enumCases, enumStats) = stats.partition(DesugarEnums.isEnumCase)
466478
if (enumCases.isEmpty)

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import typer.ConstFold
99
import reporting.trace
1010
import dotty.tools.dotc.transform.SymUtils._
1111
import Decorators._
12+
import Constants.Constant
1213

1314
import scala.annotation.tailrec
1415

@@ -396,7 +397,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
396397
case New(_) | Closure(_, _, _) =>
397398
Pure
398399
case TypeApply(fn, _) =>
399-
if (fn.symbol.is(Erased) || fn.symbol == defn.InternalQuoted_typeQuote) Pure else exprPurity(fn)
400+
if (fn.symbol.is(Erased) || fn.symbol == defn.InternalQuoted_typeQuote || fn.symbol == defn.Predef_classOf) Pure else exprPurity(fn)
400401
case Apply(fn, args) =>
401402
def isKnownPureOp(sym: Symbol) =
402403
sym.owner.isPrimitiveValueClass
@@ -512,19 +513,35 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
512513
*
513514
* { pre; v }
514515
*
516+
* (3) An expression `pre.getClass[..]()` that has a constant type `ConstantType(v)` but where
517+
* `pre` has side-effects is translated to:
518+
*
519+
* { pre; v }
520+
*
515521
* This avoids the situation where we have a Select node that does not have a symbol.
516522
*/
517523
def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
518524
val tree1 = ConstFold(tree)
519525
tree1.tpe.widenTermRefExpr.dealias.normalized match {
526+
case ConstantType(Constant(_: Type)) if tree.isInstanceOf[Block] =>
527+
// We can't rewrite `{ class A; classOf[A] }` to `classOf[A]`, so we leave
528+
// blocks returning a class literal alone, even if they're idempotent.
529+
tree1
520530
case ConstantType(value) =>
521531
if (isIdempotentExpr(tree1)) Literal(value).withSpan(tree.span)
522-
else tree1 match {
523-
case Select(qual, _) if tree1.tpe.isInstanceOf[ConstantType] =>
524-
// it's a primitive unary operator; Simplify `pre.op` to `{ pre; v }` where `v` is the value of `pre.op`
525-
Block(qual :: Nil, Literal(value)).withSpan(tree.span)
526-
case _ =>
527-
tree1
532+
else {
533+
def keepPrefix(pre: Tree) =
534+
Block(pre :: Nil, Literal(value)).withSpan(tree.span)
535+
536+
tree1 match {
537+
case Select(pre, _) if tree1.tpe.isInstanceOf[ConstantType] =>
538+
// it's a primitive unary operator; Simplify `pre.op` to `{ pre; v }` where `v` is the value of `pre.op`
539+
keepPrefix(pre)
540+
case Apply(TypeApply(Select(pre, nme.getClass_), _), Nil) =>
541+
keepPrefix(pre)
542+
case _ =>
543+
tree1
544+
}
528545
}
529546
case _ => tree1
530547
}

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,25 +1154,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11541154
}
11551155
}
11561156

1157-
/** A tree that represents the class of the erasure of type `tp`. */
1158-
def clsOf(tp: Type)(implicit ctx: Context): Tree = {
1159-
def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_)
1160-
defn.scalaClassName(tp) match {
1161-
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
1162-
case tpnme.Byte => TYPE(defn.BoxedByteModule)
1163-
case tpnme.Short => TYPE(defn.BoxedShortModule)
1164-
case tpnme.Char => TYPE(defn.BoxedCharModule)
1165-
case tpnme.Int => TYPE(defn.BoxedIntModule)
1166-
case tpnme.Long => TYPE(defn.BoxedLongModule)
1167-
case tpnme.Float => TYPE(defn.BoxedFloatModule)
1168-
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
1169-
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
1170-
case _ =>
1171-
if (ctx.erasedTypes || !tp.derivesFrom(defn.ArrayClass))
1157+
/** A tree that corresponds to `Predef.classOf[$tp]` in source */
1158+
def clsOf(tp: Type)(implicit ctx: Context): Tree =
1159+
if ctx.erasedTypes then
1160+
def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_)
1161+
defn.scalaClassName(tp) match
1162+
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
1163+
case tpnme.Byte => TYPE(defn.BoxedByteModule)
1164+
case tpnme.Short => TYPE(defn.BoxedShortModule)
1165+
case tpnme.Char => TYPE(defn.BoxedCharModule)
1166+
case tpnme.Int => TYPE(defn.BoxedIntModule)
1167+
case tpnme.Long => TYPE(defn.BoxedLongModule)
1168+
case tpnme.Float => TYPE(defn.BoxedFloatModule)
1169+
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
1170+
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
1171+
case _ =>
11721172
Literal(Constant(TypeErasure.erasure(tp)))
1173-
else Literal(Constant(tp))
1174-
}
1175-
}
1173+
else
1174+
Literal(Constant(tp))
11761175

11771176
@tailrec
11781177
def sameTypes(trees: List[tpd.Tree], trees1: List[tpd.Tree]): Boolean =

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,28 @@ object Annotations {
235235
}
236236
yield ScalaVersion.parse(arg.stringValue)
237237
}
238+
239+
/** Extracts the type of the thrown exception from an annotation.
240+
*
241+
* Supports both "old-style" `@throws(classOf[Exception])`
242+
* as well as "new-style" `@throws[Exception]("cause")` annotations.
243+
*/
244+
object ThrownException {
245+
def unapply(a: Annotation)(using Context): Option[Type] =
246+
if (a.symbol ne defn.ThrowsAnnot)
247+
None
248+
else a.argumentConstant(0) match {
249+
// old-style: @throws(classOf[Exception]) (which is throws[T](classOf[Exception]))
250+
case Some(Constant(tpe: Type)) =>
251+
Some(tpe)
252+
// new-style: @throws[Exception], @throws[Exception]("cause")
253+
case _ =>
254+
stripApply(a.tree) match {
255+
case TypeApply(_, List(tpt)) =>
256+
Some(tpt.tpe)
257+
case _ =>
258+
None
259+
}
260+
}
261+
}
238262
}

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,27 @@ class Definitions {
13091309
else parents
13101310
}
13111311

1312+
private val HasProblematicGetClass: Set[Name] = Set(
1313+
tpnme.AnyVal, tpnme.Byte, tpnme.Short, tpnme.Char, tpnme.Int, tpnme.Long, tpnme.Float, tpnme.Double,
1314+
tpnme.Unit, tpnme.Boolean)
1315+
1316+
/** When typing a primitive value class or AnyVal, we ignore the `getClass`
1317+
* member: it's supposed to be an override of the `getClass` defined on `Any`,
1318+
* but in dotty `Any#getClass` is polymorphic so it ends up being an overload.
1319+
* This is especially problematic because it means that when writing:
1320+
*
1321+
* 1.asInstanceOf[Int & AnyRef].getClass
1322+
*
1323+
* the `getClass` that returns `Class[Int]` defined in Int can be selected,
1324+
* but this call is specified to return `classOf[Integer]`, see
1325+
* tests/run/t5568.scala.
1326+
*
1327+
* FIXME: remove all the `getClass` methods defined in the standard library
1328+
* so we don't have to hot-patch it like this.
1329+
*/
1330+
def hasProblematicGetClass(className: Name): Boolean =
1331+
HasProblematicGetClass.contains(className)
1332+
13121333
/** Is synthesized symbol with alphanumeric name allowed to be used as an infix operator? */
13131334
def isInfix(sym: Symbol)(implicit ctx: Context): Boolean =
13141335
(sym eq Object_eq) || (sym eq Object_ne)

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,12 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
187187
val savedIndex = readIndex
188188
readIndex = index(i)
189189
val sym = readSymbol()
190-
entries(i) = sym
191-
sym.infoOrCompleter match {
192-
case info: ClassUnpickler => info.init()
193-
case _ =>
194-
}
190+
if sym.exists then
191+
entries(i) = sym
192+
sym.infoOrCompleter match {
193+
case info: ClassUnpickler => info.init()
194+
case _ =>
195+
}
195196
readIndex = savedIndex
196197
}
197198
i += 1
@@ -438,6 +439,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
438439
var name = at(nameref, () => readName()(ctx))
439440
val owner = readSymbolRef()
440441

442+
if (name eq nme.getClass_) && defn.hasProblematicGetClass(owner.name) then
443+
// skip this member
444+
return NoSymbol
445+
441446
var flags = unpickleScalaFlags(readLongNat(), name.isTypeName)
442447

443448
name = name.adjustIfModuleClass(flags)

compiler/src/dotty/tools/dotc/transform/ClassOf.scala

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)