Skip to content

Commit 51d0d76

Browse files
authored
Avoid creating constructors where not warranted (#23178)
Avoid creating constructors where not warranted. Fixes #15144
2 parents 66d15b2 + da86c2c commit 51d0d76

File tree

6 files changed

+45
-31
lines changed

6 files changed

+45
-31
lines changed

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,12 @@ object Primitives {
150150
case object XOR extends LogicalOp
151151

152152
/** Signals the beginning of a series of concatenations.
153-
* On the JVM platform, it should create a new StringBuffer
154-
*/
153+
* On the JVM platform, it should create a new StringBuilder.
154+
*/
155155
case object StartConcat extends Primitive
156156

157-
/**
158-
* type: (buf) => STR
159-
* jvm : It should turn the StringBuffer into a String.
157+
/** type: (buf) => STR
158+
* jvm : It should turn the StringBuilder into a String.
160159
*/
161160
case object EndConcat extends Primitive
162161

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -369,27 +369,21 @@ class ClassfileParser(
369369
* Updates the read pointer of 'in'. */
370370
def parseParents: List[Type] = {
371371
val superType =
372-
if (classRoot.symbol == defn.ComparableClass ||
373-
classRoot.symbol == defn.JavaCloneableClass ||
374-
classRoot.symbol == defn.JavaSerializableClass) {
375-
// Treat these interfaces as universal traits
376-
in.nextChar
372+
val superClass = in.nextChar
373+
// Treat these interfaces as universal traits
374+
if classRoot.symbol == defn.ComparableClass
375+
|| classRoot.symbol == defn.JavaCloneableClass
376+
|| classRoot.symbol == defn.JavaSerializableClass
377+
then
377378
defn.AnyType
378-
}
379379
else
380-
pool.getSuperClass(in.nextChar).typeRef
380+
pool.getSuperClass(superClass).typeRef
381381
val ifaceCount = in.nextChar
382-
var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef
383-
// Dotty deviation: was
384-
// var ifaces = for (i <- List.range(0, ifaceCount)) ...
385-
// This does not typecheck because the type parameter of List is now lower-bounded by Int | Char.
386-
// Consequently, no best implicit for the "Integral" evidence parameter of "range"
387-
// is found. Previously, this worked because of weak conformance, which has been dropped.
388-
382+
val ifaces = List.fill(ifaceCount.toInt):
383+
pool.getSuperClass(in.nextChar).typeRef
389384
superType :: ifaces
390385
}
391386

392-
393387
val result = unpickleOrParseInnerClasses()
394388
if (!result.isDefined) {
395389
var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol)
@@ -408,8 +402,8 @@ class ClassfileParser(
408402
moduleRoot.setPrivateWithin(privateWithin)
409403
moduleRoot.sourceModule.setPrivateWithin(privateWithin)
410404

411-
for (i <- 0 until in.nextChar) parseMember(method = false)
412-
for (i <- 0 until in.nextChar) parseMember(method = true)
405+
for (_ <- 0 until in.nextChar) parseMember(method = false)
406+
for (_ <- 0 until in.nextChar) parseMember(method = true)
413407

414408
classRoot.registerCompanion(moduleRoot.symbol)
415409
moduleRoot.registerCompanion(classRoot.symbol)

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,22 @@ object Scala2Unpickler {
6363
denot.info = PolyType.fromParams(denot.owner.typeParams, denot.info)
6464
}
6565

66-
def ensureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope)(using Context): Unit = {
67-
if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) {
68-
val constr = newDefaultConstructor(cls)
66+
def ensureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope)(using Context): Unit =
67+
doEnsureConstructor(cls, clsDenot, scope, fromScala2 = true)
68+
69+
private def doEnsureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope, fromScala2: Boolean)
70+
(using Context): Unit =
71+
if scope.lookup(nme.CONSTRUCTOR) == NoSymbol then
72+
val constr =
73+
if fromScala2 || cls.isAllOf(Trait | JavaDefined) then newDefaultConstructor(cls)
74+
else newConstructor(cls, Private, paramNames = Nil, paramTypes = Nil)
6975
// Scala 2 traits have a constructor iff they have initialization code
7076
// In dotc we represent that as !StableRealizable, which is also owner.is(NoInits)
7177
if clsDenot.flagsUNSAFE.is(Trait) then
7278
constr.setFlag(StableRealizable)
7379
clsDenot.setFlag(NoInits)
7480
addConstructorTypeParams(constr)
7581
cls.enter(constr, scope)
76-
}
77-
}
7882

7983
def setClassInfo(denot: ClassDenotation, info: Type, fromScala2: Boolean, selfInfo: Type = NoType)(using Context): Unit = {
8084
val cls = denot.classSymbol
@@ -108,7 +112,7 @@ object Scala2Unpickler {
108112
if (tsym.exists) tsym.setFlag(TypeParam)
109113
else denot.enter(tparam, decls)
110114
}
111-
if (!denot.flagsUNSAFE.isAllOf(JavaModule)) ensureConstructor(cls, denot, decls)
115+
if (!denot.flagsUNSAFE.isAllOf(JavaModule)) doEnsureConstructor(cls, denot, decls, fromScala2)
112116

113117
val scalacCompanion = denot.classSymbol.scalacLinkedClass
114118

compiler/src/dotty/tools/dotc/printing/Formatting.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ object Formatting {
144144
given Show[Class[?]] = ShowAny
145145
given Show[Throwable] = ShowAny
146146
given Show[StringBuffer] = ShowAny
147+
given Show[StringBuilder] = ShowAny
147148
given Show[CompilationUnit] = ShowAny
148149
given Show[Phases.Phase] = ShowAny
149150
given Show[TyperState] = ShowAny

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3142,18 +3142,18 @@ extends ReferenceMsg(CannotBeAccessedID):
31423142
case _ =>
31433143
i"none of the overloaded alternatives named $name can"
31443144
val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else ""
3145-
val whyNot = new StringBuffer
3145+
val whyNot = new StringBuilder
31463146
for alt <- alts do
31473147
val cls = alt.owner.enclosingSubClass
31483148
val owner = if cls.exists then cls else alt.owner
31493149
val location: String =
31503150
if alt.is(Protected) then
31513151
if alt.privateWithin.exists && alt.privateWithin != owner then
31523152
if owner.is(Final) then alt.privateWithin.showLocated
3153-
else alt.privateWithin.showLocated + ", or " + owner.showLocated + " or one of its subclasses"
3153+
else s"${alt.privateWithin.showLocated}, or ${owner.showLocated} or one of its subclasses"
31543154
else
31553155
if owner.is(Final) then owner.showLocated
3156-
else owner.showLocated + " or one of its subclasses"
3156+
else s"${owner.showLocated} or one of its subclasses"
31573157
else
31583158
alt.privateWithin.orElse(owner).showLocated
31593159
val accessMod = if alt.is(Protected) then "protected" else "private"

tests/neg/i15144.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//> using options --release 11
2+
// test: -jvm 17+
3+
// Must be tested where release target < current JVM.
4+
// Maybe a bug that ct.sym is not used if release == JVM version.
5+
6+
import java.time.Instant
7+
8+
class C:
9+
def f: Instant = new Instant // error
10+
def g: Instant = Instant() // error
11+
def p: P = new P // error
12+
13+
class P private ()
14+
15+
@main def test() = println:
16+
C().f

0 commit comments

Comments
 (0)