Skip to content

Commit ab2d6ec

Browse files
committed
Part of the fix for scala#15413 Part of the fix for scala#16983
1 parent 847eccc commit ab2d6ec

File tree

21 files changed

+507
-17
lines changed

21 files changed

+507
-17
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Compiler {
8989
new ExplicitOuter, // Add accessors to outer classes from nested ones.
9090
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
9191
new StringInterpolatorOpt, // Optimizes raw and s and f string interpolators by rewriting them to string concatenations or formats
92-
new DropBreaks) :: // Optimize local Break throws by rewriting them
92+
new DropBreaks) :: // Optimize local Break throws by rewriting them
9393
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
9494
new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_`
9595
new InlinePatterns, // Remove placeholders of inlined patterns
@@ -141,6 +141,7 @@ class Compiler {
141141
new sjs.JUnitBootstrappers, // Generate JUnit-specific bootstrapper classes for Scala.js (not enabled by default)
142142
new CollectEntryPoints, // Collect all entry points and save them in the context
143143
new CollectSuperCalls, // Find classes that are called with super
144+
new BinaryAPIAnnotations, // Makes @binaryAPI definitions public // TODO where should this phase be located?
144145
new RepeatableAnnotations) :: // Aggregate repeatable annotations
145146
Nil
146147

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ class Definitions {
10471047
@tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability")
10481048
@tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains")
10491049
@tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName")
1050+
@tu lazy val BinaryAPIAnnot: ClassSymbol = requiredClass("scala.annotation.binaryAPI")
10501051

10511052
@tu lazy val JavaRepeatableAnnot: ClassSymbol = requiredClass("java.lang.annotation.Repeatable")
10521053

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,13 @@ object SymDenotations {
10351035
isOneOf(EffectivelyErased)
10361036
|| is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)
10371037

1038+
/** Is this a member that will become public in the generated binary */
1039+
def isBinaryAPI(using Context): Boolean =
1040+
isTerm && (
1041+
hasAnnotation(defn.BinaryAPIAnnot) ||
1042+
allOverriddenSymbols.exists(_.hasAnnotation(defn.BinaryAPIAnnot))
1043+
)
1044+
10381045
/** ()T and => T types should be treated as equivalent for this symbol.
10391046
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,
10401047
* because the Scala library does not always follow the right conventions.

compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ object PrepareInlineable {
5353
/** A tree map which inserts accessors for non-public term members accessed from inlined code.
5454
*/
5555
abstract class MakeInlineableMap(val inlineSym: Symbol) extends TreeMap with Insert {
56+
57+
def useBinaryAPI: Boolean
58+
5659
def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName =
5760
val accName = InlineAccessorName(name)
5861
if site.isExtensibleClass then accName.expandedName(site) else accName
@@ -70,6 +73,7 @@ object PrepareInlineable {
7073
def needsAccessor(sym: Symbol)(using Context): Boolean =
7174
sym.isTerm &&
7275
(sym.isOneOf(AccessFlags) || sym.privateWithin.exists) &&
76+
(!useBinaryAPI || !sym.isBinaryAPI) &&
7377
!sym.isContainedIn(inlineSym) &&
7478
!(sym.isStableMember && sym.info.widenTermRefExpr.isInstanceOf[ConstantType]) &&
7579
!sym.isInlineMethod &&
@@ -100,7 +104,7 @@ object PrepareInlineable {
100104
* possible if the receiver is essentially this or an outer this, which is indicated
101105
* by the test that we can find a host for the accessor.
102106
*/
103-
class MakeInlineableDirect(inlineSym: Symbol) extends MakeInlineableMap(inlineSym) {
107+
class MakeInlineableDirect(inlineSym: Symbol, val useBinaryAPI: Boolean) extends MakeInlineableMap(inlineSym) {
104108
def preTransform(tree: Tree)(using Context): Tree = tree match {
105109
case tree: RefTree if needsAccessor(tree.symbol) =>
106110
if (tree.symbol.isConstructor) {
@@ -124,13 +128,14 @@ object PrepareInlineable {
124128
* private[inlines] def next[U](y: U): (T, U) = (x, y)
125129
* }
126130
* class TestPassing {
127-
* inline def foo[A](x: A): (A, Int) = {
128-
* val c = new C[A](x)
129-
* c.next(1)
130-
* }
131-
* inline def bar[A](x: A): (A, String) = {
132-
* val c = new C[A](x)
133-
* c.next("")
131+
* inline def foo[A](x: A): (A, Int) = {
132+
* val c = new C[A](x)
133+
* c.next(1)
134+
* }
135+
* inline def bar[A](x: A): (A, String) = {
136+
* val c = new C[A](x)
137+
* c.next("")
138+
* }
134139
* }
135140
*
136141
* `C` could be compiled separately, so we cannot place the inline accessor in it.
@@ -143,7 +148,7 @@ object PrepareInlineable {
143148
* Since different calls might have different receiver types, we need to generate one
144149
* such accessor per call, so they need to have unique names.
145150
*/
146-
class MakeInlineablePassing(inlineSym: Symbol) extends MakeInlineableMap(inlineSym) {
151+
class MakeInlineablePassing(inlineSym: Symbol, val useBinaryAPI: Boolean) extends MakeInlineableMap(inlineSym) {
147152

148153
def preTransform(tree: Tree)(using Context): Tree = tree match {
149154
case _: Apply | _: TypeApply | _: RefTree
@@ -153,7 +158,7 @@ object PrepareInlineable {
153158
val qual = qualifier(refPart)
154159
inlining.println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, $argss%, %")
155160

156-
// Need to dealias in order to cagtch all possible references to abstracted over types in
161+
// Need to dealias in order to catch all possible references to abstracted over types in
157162
// substitutions
158163
val dealiasMap = new TypeMap {
159164
def apply(t: Type) = mapOver(t.dealias)
@@ -226,8 +231,13 @@ object PrepareInlineable {
226231
// so no accessors are needed for them.
227232
tree
228233
else
229-
new MakeInlineablePassing(inlineSym).transform(
230-
new MakeInlineableDirect(inlineSym).transform(tree))
234+
// Make sure the old accessors are generated for binary compatibility
235+
new MakeInlineablePassing(inlineSym, useBinaryAPI = false).transform(
236+
new MakeInlineableDirect(inlineSym, useBinaryAPI = false).transform(tree))
237+
238+
// TODO: warn if MakeInlineablePassing or MakeInlineableDirect generate accessors when useBinaryAPI in enabled
239+
new MakeInlineablePassing(inlineSym, useBinaryAPI = true).transform(
240+
new MakeInlineableDirect(inlineSym, useBinaryAPI = true).transform(tree))
231241
}
232242
}
233243

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools.dotc.transform
2+
3+
import dotty.tools.dotc.core.Contexts.*
4+
import dotty.tools.dotc.core.DenotTransformers.SymTransformer
5+
import dotty.tools.dotc.core.Flags.*
6+
import dotty.tools.dotc.core.Symbols.NoSymbol
7+
import dotty.tools.dotc.core.SymDenotations.SymDenotation
8+
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
9+
10+
/** Makes @binaryAPI definitions public */
11+
class BinaryAPIAnnotations extends MiniPhase with SymTransformer:
12+
13+
override def phaseName: String = BinaryAPIAnnotations.name
14+
override def description: String = BinaryAPIAnnotations.description
15+
16+
def transformSym(d: SymDenotation)(using Context): SymDenotation = {
17+
if d.isBinaryAPI then
18+
d.resetFlag(Protected)
19+
d.setPrivateWithin(NoSymbol)
20+
d
21+
}
22+
23+
object BinaryAPIAnnotations:
24+
val name: String = "binaryAPIAnnotations"
25+
val description: String = "makes @binaryAPI definitions public"

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,10 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
167167
if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass)
168168
else transformFollowingDeep(ref(field))(using ctx.withOwner(sym))
169169
val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
170-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot))
170+
val binaryAPIAnnotOpt = sym.getAnnotation(defn.BinaryAPIAnnot)
171+
// FIXME: copyAndKeepAnnotationsCarrying is dropping defn.BinaryAPIAnnot
172+
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.BinaryAPIAnnot))
173+
for binaryAPIAnnot <- binaryAPIAnnotOpt do sym.addAnnotation(binaryAPIAnnot)
171174
Thicket(fieldDef, getterDef)
172175
else if sym.isSetter then
173176
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion
@@ -193,7 +196,10 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
193196
then Literal(Constant(()))
194197
else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol)))
195198
val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym)))
196-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
199+
val binaryAPIAnnotOpt = sym.getAnnotation(defn.BinaryAPIAnnot)
200+
// FIXME: copyAndKeepAnnotationsCarrying is dropping defn.BinaryAPIAnnot
201+
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot, defn.BinaryAPIAnnot))
202+
for binaryAPIAnnot <- binaryAPIAnnotOpt do sym.addAnnotation(binaryAPIAnnot)
197203
setterDef
198204
else
199205
// Curiously, some accessors from Scala2 have ' ' suffixes.

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
159159
if sym.isSetter then
160160
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
161161
else
162+
val binaryAPIAnnotOpt = sym.getAnnotation(defn.BinaryAPIAnnot)
162163
if sym.is(Param) then
163164
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
164165
else if sym.is(ParamAccessor) then
165-
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot))
166+
// FIXME: copyAndKeepAnnotationsCarrying is dropping defn.BinaryAPIAnnot
167+
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot, defn.BinaryAPIAnnot))
168+
for binaryAPIAnnot <- binaryAPIAnnotOpt do sym.addAnnotation(binaryAPIAnnot)
166169
else
167170
sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots)
168171
if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,11 @@ object Checking {
526526
fail(em"Inline methods cannot be @tailrec")
527527
if sym.hasAnnotation(defn.TargetNameAnnot) && sym.isClass && sym.isTopLevelClass then
528528
fail(TargetNameOnTopLevelClass(sym))
529+
if sym.hasAnnotation(defn.BinaryAPIAnnot) then
530+
if sym.is(Enum) then fail(em"@binaryAPI cannot be used on enum definitions.")
531+
else if sym.isType && !(sym.is(Given) || sym.companionModule.is(Given)) then fail(em"@binaryAPI cannot be used on ${sym.showKind} definitions")
532+
else if sym.isLocal && !sym.is(Param) then fail(em"@binaryAPI cannot be used on local definitions.")
533+
else if sym.is(Private) then fail(em"@binaryAPI cannot be used on private definitions.\n\nCould use private[${sym.owner.name}] or protected instead.")
529534
if (sym.hasAnnotation(defn.NativeAnnot)) {
530535
if (!sym.is(Deferred))
531536
fail(NativeMembersMayNotHaveImplementation(sym))

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ trait TypeAssigner {
9999
val tpe1 = accessibleType(tpe, superAccess)
100100
if tpe1.exists then tpe1
101101
else tpe match
102-
case tpe: NamedType => inaccessibleErrorType(tpe, superAccess, pos)
102+
case tpe: NamedType =>
103+
if tpe.termSymbol.isBinaryAPI then tpe
104+
else inaccessibleErrorType(tpe, superAccess, pos)
103105
case NoType => tpe
104106

105107
/** Return a potentially skolemized version of `qualTpe` to be used

0 commit comments

Comments
 (0)