|
1 |
| -package dotty.tools.dotc |
| 1 | +package dotty.tools |
| 2 | +package dotc |
2 | 3 | package transform
|
3 | 4 |
|
4 | 5 | import core.*
|
@@ -180,26 +181,47 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co
|
180 | 181 | if enclClass.isContainedIn(thisClass) then thisClass
|
181 | 182 | else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
|
182 | 183 |
|
| 184 | + /** Set the first owner of a local method or class that's nested inside a term. |
| 185 | + * This is either the enclosing package or the enclosing class. If the former, |
| 186 | + * the method will be be translated to a static method of its toplevel class. |
| 187 | + * In that case, we might later re-adjust the owner to a nested class via |
| 188 | + * `narrowTo` when we see that the method refers to the this-type of that class. |
| 189 | + * We choose the enclosing package when there's something potentially to gain from this |
| 190 | + * and when it is safe to do so |
| 191 | + */ |
183 | 192 | def setLogicOwner(local: Symbol) =
|
184 | 193 | val encClass = local.owner.enclosingClass
|
| 194 | + // When to prefer the enclosing class over the enclosing package: |
185 | 195 | val preferEncClass =
|
186 |
| - ( |
187 | 196 | encClass.isStatic
|
188 |
| - // non-static classes can capture owners, so should be avoided |
| 197 | + // If class is not static, we try to hoist the method out of |
| 198 | + // the class to avoid the outer pointer. |
189 | 199 | && (encClass.isProperlyContainedIn(local.topLevelClass)
|
190 |
| - // can be false for symbols which are defined in some weird combination of supercalls. |
| 200 | + // If class is nested in an outer object, we prefer to leave the method in the class, |
| 201 | + // since putting it in the outer object makes access more complicated |
191 | 202 | || encClass.is(ModuleClass, butNot = Package)
|
192 |
| - // needed to not cause deadlocks in classloader. see t5375.scala |
| 203 | + // If class is an outermost object we also want to avoid making the |
| 204 | + // method static since that could cause deadlocks in interacting |
| 205 | + // with class initialization. See deadlock.scala |
193 | 206 | )
|
194 |
| - ) |
195 |
| - || ( |
| 207 | + && (!sym.isAnonymousFunction || sym.owner.ownersIterator.exists(_.isConstructor)) |
| 208 | + // The previous conditions mean methods in static objects and nested static classes |
| 209 | + // don't get lifted out to be static. In general it is prudent to do that. However, |
| 210 | + // for anonymous functions, we prefer them to be static because that means lambdas |
| 211 | + // are memoized and can be serialized even if the enclosing object or class |
| 212 | + // is not serializable. See run/lambda-serialization-gc.scala and run/i19224.scala. |
| 213 | + // On the other hand, we don't want to lift anonymous functions from inside the |
| 214 | + // object or class constructor to be static since that can cause again deadlocks |
| 215 | + // by its interaction with class initialization. See run/deadlock.scala, which works |
| 216 | + // in Scala 3 but deadlocks in Scala 2. |
| 217 | + || |
196 | 218 | /* Scala.js: Never move any member beyond the boundary of a DynamicImportThunk.
|
197 | 219 | * DynamicImportThunk subclasses are boundaries between the eventual ES modules
|
198 | 220 | * that can be dynamically loaded. Moving members across that boundary changes
|
199 | 221 | * the dynamic and static dependencies between ES modules, which is forbidden.
|
200 | 222 | */
|
201 | 223 | ctx.settings.scalajs.value && encClass.isSubClass(jsdefn.DynamicImportThunkClass)
|
202 |
| - ) |
| 224 | + |
203 | 225 | logicOwner(sym) = if preferEncClass then encClass else local.enclosingPackageClass
|
204 | 226 |
|
205 | 227 | tree match
|
|
0 commit comments