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