summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
blob: f7ee36c1ba0a54b1ac93538052d006cdc54da0fb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
/* NSC -- new Scala compiler
 * Copyright 2005-2014 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package backend.jvm

import scala.tools.asm
import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
import scala.tools.nsc.backend.jvm.BTypes._
import BackendReporting._
import scala.tools.nsc.settings.ScalaSettings
import scala.reflect.internal.Flags.{DEFERRED, SYNTHESIZE_IMPL_IN_SUBCLASS}

/**
 * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
 * information from a symbol and its type to create the corresponding ClassBType. It requires
 * access to the compiler (global parameter).
 *
 * The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes
 * uses classBTypeFromSymbol, hence requires access to the compiler (global).
 *
 * BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some
 * of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
 * not have access to the compiler instance.
 */
class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
  import global._
  import definitions._
  import genBCode._

  val backendUtils: BackendUtils[this.type] = new BackendUtils(this)

  // Why the proxy, see documentation of class [[CoreBTypes]].
  val coreBTypes = new CoreBTypesProxy[this.type](this)
  import coreBTypes._

  val byteCodeRepository: ByteCodeRepository[this.type] = new ByteCodeRepository(global.optimizerClassPath(global.classPath), this)

  val localOpt: LocalOpt[this.type] = new LocalOpt(this)

  val inliner: Inliner[this.type] = new Inliner(this)

  val inlinerHeuristics: InlinerHeuristics[this.type] = new InlinerHeuristics(this)

  val closureOptimizer: ClosureOptimizer[this.type] = new ClosureOptimizer(this)

  val callGraph: CallGraph[this.type] = new CallGraph(this)

  val backendReporting: BackendReporting = new BackendReportingImpl(global)

  final def initializeCoreBTypes(): Unit = {
    coreBTypes.setBTypes(new CoreBTypes[this.type](this))
  }

  def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T = perRunCaches.recordCache(cache)

  def compilerSettings: ScalaSettings = settings

  // helpers that need access to global.
  // TODO @lry create a separate component, they don't belong to BTypesFromSymbols

  final val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString

  private val primitiveCompilationUnits = Set(
    "Unit.scala",
    "Boolean.scala",
    "Char.scala",
    "Byte.scala",
    "Short.scala",
    "Int.scala",
    "Float.scala",
    "Long.scala",
    "Double.scala"
  )

  /**
   * True if the current compilation unit is of a primitive class (scala.Boolean et al).
   * Used only in assertions.
   */
  def isCompilingPrimitive = {
    primitiveCompilationUnits(currentUnit.source.file.name)
  }

  def isCompilingArray = {
    currentUnit.source.file.name == "Array.scala"
  }

  // end helpers

  /**
   * The ClassBType for a class symbol `classSym`.
   *
   * The class symbol scala.Nothing is mapped to the class scala.runtime.Nothing$. Similarly,
   * scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files
   * for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
   * in the classfile method signature.
   */
  final def classBTypeFromSymbol(sym: Symbol): ClassBType = {
    // For each java class, the scala compiler creates a class and a module (thus a module class).
    // If the `sym` is a java module class, we use the java class instead. This ensures that the
    // ClassBType is created from the main class (instead of the module class).
    // The two symbols have the same name, so the resulting internalName is the same.
    // Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
    val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym

    assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
    assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
    assertClassNotArrayNotPrimitive(classSym)
    assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym")

    if (classSym == NothingClass) srNothingRef
    else if (classSym == NullClass) srNullRef
    else {
      val internalName = classSym.javaBinaryNameString
      classBTypeFromInternalName.getOrElse(internalName, {
        // The new ClassBType is added to the map in its constructor, before we set its info. This
        // allows initializing cyclic dependencies, see the comment on variable ClassBType._info.
        val res = ClassBType(internalName)
        if (completeSilentlyAndCheckErroneous(classSym)) {
          res.info = Left(NoClassBTypeInfoClassSymbolInfoFailedSI9111(classSym.fullName))
          res
        } else {
          setClassInfo(classSym, res)
        }
      })
    }
  }

  /**
   * Builds a [[MethodBType]] for a method symbol.
   */
  final def methodBTypeFromSymbol(methodSymbol: Symbol): MethodBType = {
    assert(methodSymbol.isMethod, s"not a method-symbol: $methodSymbol")
    methodBTypeFromMethodType(methodSymbol.info, methodSymbol.isClassConstructor || methodSymbol.isConstructor)
  }

  /**
   * Builds a [[MethodBType]] for a method type.
   */
  final def methodBTypeFromMethodType(tpe: Type, isConstructor: Boolean): MethodBType = {
    val resultType: BType =
      if (isConstructor) UNIT
      else typeToBType(tpe.resultType)
    MethodBType(tpe.paramTypes map typeToBType, resultType)
  }

  def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
    case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
    case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
    case c @ Constant(value: String) => value
    case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
    case _ => reporter.error(pos, "Unable to convert static argument of ApplyDynamic into a classfile constant: " + t); null
  }

  def staticHandleFromSymbol(sym: Symbol): asm.Handle = {
    val owner = if (sym.owner.isModuleClass) sym.owner.linkedClassOfClass else sym.owner
    val descriptor = methodBTypeFromMethodType(sym.info, isConstructor = false).descriptor
    val ownerBType = classBTypeFromSymbol(owner)
    new asm.Handle(asm.Opcodes.H_INVOKESTATIC, ownerBType.internalName, sym.name.encoded, descriptor, /* itf = */ ownerBType.isInterface.get)
  }

  /**
   * This method returns the BType for a type reference, for example a parameter type.
   */
  final def typeToBType(t: Type): BType = {
    import definitions.ArrayClass

    /**
     * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int.
     * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType.
     */
    def primitiveOrClassToBType(sym: Symbol): BType = {
      assertClassNotArray(sym)
      primitiveTypeToBType.getOrElse(sym, classBTypeFromSymbol(sym))
    }

    /**
     * When compiling Array.scala, the type parameter T is not erased and shows up in method
     * signatures, e.g. `def apply(i: Int): T`. A TypeRef for T is replaced by ObjectRef.
     */
    def nonClassTypeRefToBType(sym: Symbol): ClassBType = {
      assert(sym.isType && isCompilingArray, sym)
      ObjectRef
    }

    t.dealiasWiden match {
      case TypeRef(_, ArrayClass, List(arg))  => ArrayBType(typeToBType(arg)) // Array type such as Array[Int] (kept by erasure)
      case TypeRef(_, sym, _) if !sym.isClass => nonClassTypeRefToBType(sym)  // See comment on nonClassTypeRefToBType
      case TypeRef(_, sym, _)                 => primitiveOrClassToBType(sym) // Common reference to a type such as scala.Int or java.lang.String
      case ClassInfoType(_, _, sym)           => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes typeToBType(moduleClassSymbol.info)

      /* The cases below should probably never occur. They are kept for now to avoid introducing
       * new compiler crashes, but we added a warning. The compiler / library bootstrap and the
       * test suite don't produce any warning.
       */

      case tp =>
        warning(tp.typeSymbol.pos,
          s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " +
            "If possible, please file a bug on issues.scala-lang.org.")

        tp match {
          case ThisType(ArrayClass)    => ObjectRef // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
          case ThisType(sym)           => classBTypeFromSymbol(sym)
          case SingleType(_, sym)      => primitiveOrClassToBType(sym)
          case ConstantType(_)         => typeToBType(t.underlying)
          case RefinedType(parents, _) => parents.map(typeToBType(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b).get)
          case AnnotatedType(_, t)     => typeToBType(t)
          case ExistentialType(_, t)   => typeToBType(t)
        }
    }
  }

  def assertClassNotArray(sym: Symbol): Unit = {
    assert(sym.isClass, sym)
    assert(sym != definitions.ArrayClass || isCompilingArray, sym)
  }

  def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = {
    assertClassNotArray(sym)
    assert(!primitiveTypeToBType.contains(sym) || isCompilingPrimitive, sym)
  }

  def implementedInterfaces(classSym: Symbol): List[Symbol] = {
    // Additional interface parents based on annotations and other cues
    def newParentForAnnotation(ann: AnnotationInfo): Option[Type] = ann.symbol match {
      case RemoteAttr => Some(RemoteInterfaceClass.tpe)
      case _          => None
    }

    // SI-9393: java annotations are interfaces, but the classfile / java source parsers make them look like classes.
    def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait || sym.hasJavaAnnotationFlag

    val classParents = {
      val parents = classSym.info.parents
      // SI-9393: the classfile / java source parsers add Annotation and ClassfileAnnotation to the
      // parents of a java annotations. undo this for the backend (where we need classfile-level information).
      if (classSym.hasJavaAnnotationFlag) parents.filterNot(c => c.typeSymbol == ClassfileAnnotationClass || c.typeSymbol == AnnotationClass)
      else parents
    }

    val allParents = classParents ++ classSym.annotations.flatMap(newParentForAnnotation)

    val minimizedParents = if (classSym.isJavaDefined) allParents else erasure.minimizeParents(allParents)
    // We keep the superClass when computing minimizeParents to eliminate more interfaces.
    // Example: T can be eliminated from D
    //   trait T
    //   class C extends T
    //   class D extends C with T
    val interfaces = minimizedParents match {
      case superClass :: ifs if !isInterfaceOrTrait(superClass.typeSymbol) =>
        ifs
      case ifs =>
        // minimizeParents removes the superclass if it's redundant, for example:
        //  trait A
        //  class C extends Object with A  // minimizeParents removes Object
        ifs
    }
    interfaces.map(_.typeSymbol)
  }

  /**
   * The member classes of a class symbol. Note that the result of this method depends on the
   * current phase, for example, after lambdalift, all local classes become member of the enclosing
   * class.
   *
   * Specialized classes are always considered top-level, see comment in BTypes.
   */
  private def memberClassesForInnerClassTable(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
    case sym if sym.isClass && !considerAsTopLevelImplementationArtifact(sym) =>
      sym
    case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) =>
      val r = exitingPickler(sym.moduleClass)
      assert(r != NoSymbol, sym.fullLocationString)
      r
  })(collection.breakOut)

  private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
    /**
     * Reconstruct the classfile flags from a Java defined class symbol.
     *
     * The implementation of this method is slightly different from `javaFlags` in BTypesFromSymbols.
     * The javaFlags method is primarily used to map Scala symbol flags to sensible classfile flags
     * that are used in the generated classfiles. For example, all classes emitted by the Scala
     * compiler have ACC_PUBLIC.
     *
     * When building a [[ClassBType]] from a Java class symbol, the flags in the type's `info` have
     * to correspond exactly to the flags in the classfile. For example, if the class is package
     * protected (i.e., it doesn't have the ACC_PUBLIC flag), this needs to be reflected in the
     * ClassBType. For example, the inliner needs the correct flags for access checks.
     *
     * Class flags are listed here:
     *   https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1
     */
    def javaClassfileFlags(classSym: Symbol): Int = {
      assert(classSym.isJava, s"Expected Java class symbol, got ${classSym.fullName}")
      import asm.Opcodes._
      def enumFlags = ACC_ENUM | {
        // Java enums have the `ACC_ABSTRACT` flag if they have a deferred method.
        // We cannot trust `hasAbstractFlag`: the ClassfileParser adds `ABSTRACT` and `SEALED` to all
        // Java enums for exhaustiveness checking.
        val hasAbstractMethod = classSym.info.decls.exists(s => s.isMethod && s.isDeferred)
        if (hasAbstractMethod) ACC_ABSTRACT else 0
      }
      GenBCode.mkFlags(
        // SI-9393: the classfile / java source parser make java annotation symbols look like classes.
        // here we recover the actual classfile flags.
        if (classSym.hasJavaAnnotationFlag)                        ACC_ANNOTATION | ACC_INTERFACE | ACC_ABSTRACT else 0,
        if (classSym.isPublic)                                     ACC_PUBLIC    else 0,
        if (classSym.isFinal)                                      ACC_FINAL     else 0,
        // see the link above. javac does the same: ACC_SUPER for all classes, but not interfaces.
        if (classSym.isInterface)                                  ACC_INTERFACE else ACC_SUPER,
        // for Java enums, we cannot trust `hasAbstractFlag` (see comment in enumFlags)
        if (!classSym.hasJavaEnumFlag && classSym.hasAbstractFlag) ACC_ABSTRACT  else 0,
        if (classSym.isArtifact)                                   ACC_SYNTHETIC else 0,
        if (classSym.hasJavaEnumFlag)                              enumFlags     else 0
      )
    }

    // Check for hasAnnotationFlag for SI-9393: the classfile / java source parsers add
    // scala.annotation.Annotation as superclass to java annotations. In reality, java
    // annotation classfiles have superclass Object (like any interface classfile).
    val superClassSym = if (classSym.hasJavaAnnotationFlag) ObjectClass else {
      val sc = classSym.superClass
      // SI-9393: Java annotation classes don't have the ABSTRACT/INTERFACE flag, so they appear
      // (wrongly) as superclasses. Fix this for BTypes: the java annotation will appear as interface
      // (handled by method implementedInterfaces), the superclass is set to Object.
      if (sc.hasJavaAnnotationFlag) ObjectClass
      else sc
    }
    assert(
      if (classSym == ObjectClass)
        superClassSym == NoSymbol
      else if (classSym.isInterface)
        superClassSym == ObjectClass
      else
        // A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes.
        ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeToBType.contains(classSym)),
      s"Bad superClass for $classSym: $superClassSym"
    )
    val superClass = if (superClassSym == NoSymbol) None
                     else Some(classBTypeFromSymbol(superClassSym))

    val interfaces = implementedInterfaces(classSym).map(classBTypeFromSymbol)

    val flags = {
      if (classSym.isJava) javaClassfileFlags(classSym) // see comment on javaClassfileFlags
      else javaFlags(classSym)
    }

    /* The InnerClass table of a class C must contain all nested classes of C, even if they are only
     * declared but not otherwise referenced in C (from the bytecode or a method / field signature).
     * We collect them here.
     */
    val nestedClassSymbols = {
      val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases

      // The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
      // member classes right after lambdalift, we obtain all nested classes, including local and
      // anonymous ones.
      val nestedClasses = {
        val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(classSym))
        val nested = {
          // Classes nested in value classes are nested in the companion at this point. For InnerClass /
          // EnclosingMethod, we use the value class as the outer class. So we remove nested classes
          // from the companion that were originally nested in the value class.
          if (exitingPickler(linkedClass.isDerivedValueClass)) allNested.filterNot(classOriginallyNestedInClass(_, linkedClass))
          else allNested
        }

        if (isTopLevelModuleClass(classSym)) {
          // For Java compatibility, member classes of top-level objects are treated as members of
          // the top-level companion class, see comment below.
          val members = exitingPickler(memberClassesForInnerClassTable(classSym))
          nested diff members
        } else {
          nested
        }
      }

      val companionModuleMembers = if (considerAsTopLevelImplementationArtifact(classSym)) Nil else {
        // If this is a top-level class, the member classes of the companion object are added as
        // members of the class. For example:
        //   class C { }
        //   object C {
        //     class D
        //     def f = { class E }
        //   }
        // The class D is added as a member of class C. The reason is: for Java compatibility, the
        // InnerClass attribute for D has "C" (NOT the module class "C$") as the outer class of D
        // (done by buildNestedInfo). See comment in BTypes.
        // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
        // like D is a member of C, not C$.
        val javaCompatMembers = {
          if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
          // phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member
          // classes, not local classes of the companion module (E in the example) that were lifted by lambdalift.
            exitingPickler(memberClassesForInnerClassTable(linkedClass))
          else
            Nil
        }

        // Classes nested in value classes are nested in the companion at this point. For InnerClass /
        // EnclosingMethod we use the value class as enclosing class. Here we search nested classes
        // in the companion that were originally nested in the value class, and we add them as nested
        // in the value class.
        val valueClassCompanionMembers = {
          if (linkedClass != NoSymbol && exitingPickler(classSym.isDerivedValueClass)) {
            val moduleMemberClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(linkedClass))
            moduleMemberClasses.filter(classOriginallyNestedInClass(_, classSym))
          } else
            Nil
        }

        javaCompatMembers ++ valueClassCompanionMembers
      }

      nestedClasses ++ companionModuleMembers
    }

    /**
     * For nested java classes, the scala compiler creates both a class and a module (and therefore
     * a module class) symbol. For example, in `class A { class B {} }`, the nestedClassSymbols
     * for A contain both the class B and the module class B.
     * Here we get rid of the module class B, making sure that the class B is present.
     */
    val nestedClassSymbolsNoJavaModuleClasses = nestedClassSymbols.filter(s => {
      if (s.isJavaDefined && s.isModuleClass) {
        // We could also search in nestedClassSymbols for s.linkedClassOfClass, but sometimes that
        // returns NoSymbol, so it doesn't work.
        val nb = nestedClassSymbols.count(mc => mc.name == s.name && mc.owner == s.owner)
        assert(nb == 2, s"Java member module without member class: $s - $nestedClassSymbols")
        false
      } else true
    })

    val nestedClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)

    val nestedInfo = buildNestedInfo(classSym)

    val inlineInfo = buildInlineInfo(classSym, classBType.internalName)

    classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
    classBType
  }

  private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
    assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")

    val isTopLevel = innerClassSym.rawowner.isPackageClass
    // specialized classes are considered top-level, see comment in BTypes
    if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None
    else if (innerClassSym.rawowner.isTerm) {
      // This case should never be reached: the lambdalift phase mutates the rawowner field of all
      // classes to be the enclosing class. SI-9392 shows an errant macro that leaves a reference
      // to a local class symbol that no longer exists, which is not updated by lambdalift.
      devWarning(innerClassSym.pos,
        s"""The class symbol $innerClassSym with the term symbol ${innerClassSym.rawowner} as `rawowner` reached the backend.
           |Most likely this indicates a stale reference to a non-existing class introduced by a macro, see SI-9392.""".stripMargin)
      None
    } else {
      // See comment in BTypes, when is a class marked static in the InnerClass table.
      val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)

      // After lambdalift (which is where we are), the rawowner field contains the enclosing class.
      val enclosingClass = {
        // (1) Example java source: class C { static class D { } }
        // The Scala compiler creates a class and a module symbol for C. Because D is a static
        // nested class, the symbol for D is nested in the module class C (not in the class C).
        // For the InnerClass attribute, we use the class symbol C, which represents the situation
        // in the source code.

        // (2) Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
        if ((innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) ||                      // (1)
            (!isAnonymousOrLocalClass(innerClassSym) && isTopLevelModuleClass(innerClassSym.rawowner))) { // (2)
          // phase travel for linkedCoC - does not always work in late phases
          exitingPickler(innerClassSym.rawowner.linkedClassOfClass) match {
            case NoSymbol =>
              // For top-level modules without a companion class, see doc of mirrorClassClassBType.
              mirrorClassClassBType(exitingPickler(innerClassSym.rawowner))

            case companionClass =>
              classBTypeFromSymbol(companionClass)
          }
        } else {
          classBTypeFromSymbol(innerClassSym.rawowner)
        }
      }

      val outerName: Option[String] = {
        if (isAnonymousOrLocalClass(innerClassSym)) None
        else Some(enclosingClass.internalName)
      }

      val innerName: Option[String] = {
        // phase travel necessary: after flatten, the name includes the name of outer classes.
        // if some outer name contains $anon, a non-anon class is considered anon.
        if (exitingPickler(innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction)) None
        else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
      }

      Some(NestedInfo(enclosingClass, outerName, innerName, isStaticNestedClass))
    }
  }

  /**
   * Build the InlineInfo for a ClassBType from the class symbol.
   *
   * Note that the InlineInfo is only built from the symbolic information for classes that are being
   * compiled. For all other classes we delegate to inlineInfoFromClassfile. The reason is that
   * mixed-in methods are only added to class symbols being compiled, but not to other classes
   * extending traits. Creating the InlineInfo from the symbol would prevent these mixins from being
   * inlined.
   *
   * So for classes being compiled, the InlineInfo is created here and stored in the ScalaInlineInfo
   * classfile attribute.
   */
  private def buildInlineInfo(classSym: Symbol, internalName: InternalName): InlineInfo = {
    def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym)

    // phase travel required, see implementation of `compiles`. for nested classes, it checks if the
    // enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level,
    // so `compiles` would return `false`.
    if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol    // InlineInfo required for classes being compiled, we have to create the classfile attribute
    else if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
    else {
      // For classes not being compiled, the InlineInfo is read from the classfile attribute. This
      // fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class
      // symbols being compiled. For non-compiled classes, we could not build MethodInlineInfos
      // for those mixin members, which prevents inlining.
      byteCodeRepository.classNode(internalName) match {
        case Right(classNode) =>
          inlineInfoFromClassfile(classNode)
        case Left(missingClass) =>
          EmptyInlineInfo.copy(warning = Some(ClassNotFoundWhenBuildingInlineInfoFromSymbol(missingClass)))
      }
    }
  }

  /**
   * Build the [[InlineInfo]] for a class symbol.
   */
  def buildInlineInfoFromClassSymbol(classSym: Symbol): InlineInfo = {
    val isEffectivelyFinal = classSym.isEffectivelyFinal

    val sam = {
      if (classSym.isEffectivelyFinal) None
      else {
        // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
        // empty parameter list in uncurry and would therefore be picked as SAM.
        // Similarly, the fields phases adds abstract trait setters, which should not be considered
        // abstract for SAMs (they do disqualify the SAM from LMF treatment,
        // but an anonymous subclasss can be spun up by scalac after making just the single abstract method concrete)
        val samSym = exitingPickler(definitions.samOf(classSym.tpe))
        if (samSym == NoSymbol) None
        else Some(samSym.javaSimpleName.toString + methodBTypeFromSymbol(samSym).descriptor)
      }
    }

    var warning = Option.empty[ClassSymbolInfoFailureSI9111]

    def keepMember(sym: Symbol) = sym.isMethod && !scalaPrimitives.isPrimitive(sym)
    val classMethods = classSym.info.decls.iterator.filter(keepMember)
    val methods = if (!classSym.isJavaDefined) classMethods else {
      val staticMethods = classSym.companionModule.info.decls.iterator.filter(m => !m.isConstructor && keepMember(m))
      staticMethods ++ classMethods
    }

    // Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
    // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
    val methodInlineInfos = methods.flatMap({
      case methodSym =>
        if (completeSilentlyAndCheckErroneous(methodSym)) {
          // Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
          if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
          warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
          Nil
        } else {
          val name      = methodSym.javaSimpleName.toString // same as in genDefDef
          val signature = name + methodBTypeFromSymbol(methodSym).descriptor

          // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
          // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
          // so they are not marked final in the InlineInfo attribute.
          //
          // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
          // work, the abstract accessor for O will be marked effectivelyFinal.
          val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !(methodSym hasFlag DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS)

          val info = MethodInlineInfo(
            effectivelyFinal  = effectivelyFinal,
            annotatedInline   = methodSym.hasAnnotation(ScalaInlineClass),
            annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass))

          if (needsStaticImplMethod(methodSym)) {
            val staticName = traitSuperAccessorName(methodSym).toString
            val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
            val staticMethodType = methodSym.info match {
              case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
            }
            val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
            val staticMethodInfo = MethodInlineInfo(
              effectivelyFinal  = true,
              annotatedInline   = info.annotatedInline,
              annotatedNoInline = info.annotatedNoInline)
            if (methodSym.isMixinConstructor)
              List((staticMethodSignature, staticMethodInfo))
            else
              List((signature, info), (staticMethodSignature, staticMethodInfo))
          } else
            List((signature, info))
        }
    }).toMap

    InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
  }

  /**
   * For top-level objects without a companion class, the compiler generates a mirror class with
   * static forwarders (Java compat). There's no symbol for the mirror class, but we still need a
   * ClassBType (its info.nestedClasses will hold the InnerClass entries, see comment in BTypes).
   */
  def mirrorClassClassBType(moduleClassSym: Symbol): ClassBType = {
    assert(isTopLevelModuleClass(moduleClassSym), s"not a top-level module class: $moduleClassSym")
    val internalName = moduleClassSym.javaBinaryNameString.stripSuffix(nme.MODULE_SUFFIX_STRING)
    classBTypeFromInternalName.getOrElse(internalName, {
      val c = ClassBType(internalName)
      // class info consistent with BCodeHelpers.genMirrorClass
      val nested = exitingPickler(memberClassesForInnerClassTable(moduleClassSym)) map classBTypeFromSymbol
      c.info = Right(ClassInfo(
        superClass = Some(ObjectRef),
        interfaces = Nil,
        flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL,
        nestedClasses = nested,
        nestedInfo = None,
        inlineInfo = EmptyInlineInfo.copy(isEffectivelyFinal = true))) // no method inline infos needed, scala never invokes methods on the mirror class
      c
    })
  }

  def beanInfoClassClassBType(mainClass: Symbol): ClassBType = {
    val internalName = mainClass.javaBinaryNameString + "BeanInfo"
    classBTypeFromInternalName.getOrElse(internalName, {
      val c = ClassBType(internalName)
      c.info = Right(ClassInfo(
        superClass = Some(sbScalaBeanInfoRef),
        interfaces = Nil,
        flags = javaFlags(mainClass),
        nestedClasses = Nil,
        nestedInfo = None,
        inlineInfo = EmptyInlineInfo))
      c
    })
  }

  /**
   * True for module classes of package level objects. The backend will generate a mirror class for
   * such objects.
   */
  final def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
    // phase travel to pickler required for isNestedClass (looks at owner)
    sym.isModuleClass && !sym.isNestedClass
  }

  /**
   * True for module classes of modules that are top-level or owned only by objects. Module classes
   * for such objects will get a MODULE$ field and a corresponding static initializer.
   */
  final def isStaticModuleClass(sym: Symbol): Boolean = {
    sym.isModuleClass &&
    isOriginallyStaticOwner(sym.originalOwner) // isStaticModuleClass is a source-level property, see comment on isOriginallyStaticOwner
  }

  // legacy, to be removed when the @remote annotation gets removed
  final def isRemote(s: Symbol) = s hasAnnotation definitions.RemoteAttr
  final def hasPublicBitSet(flags: Int) = (flags & asm.Opcodes.ACC_PUBLIC) != 0

  /**
   * Return the Java modifiers for the given symbol.
   * Java modifiers for classes:
   *  - public, abstract, final, strictfp (not used)
   * for interfaces:
   *  - the same as for classes, without 'final'
   * for fields:
   *  - public, private (*)
   *  - static, final
   * for methods:
   *  - the same as for fields, plus:
   *  - abstract, synchronized (not used), strictfp (not used), native (not used)
   * for all:
   *  - deprecated
   *
   *  (*) protected cannot be used, since inner classes 'see' protected members,
   *      and they would fail verification after lifted.
   */
  final def javaFlags(sym: Symbol): Int = {
    // constructors of module classes should be private. introduced in b06edbc, probably to prevent
    // creating module instances from java. for nested modules, the constructor needs to be public
    // since they are created by the outer class and stored in a field. a java client can create
    // new instances via outerClassInstance.new InnerModuleClass$().
    // TODO: do this early, mark the symbol private.
    val privateFlag =
      sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))

    // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
    // added to modules and module classes, not anymore since 296b706).
    // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
    // ACC_FINAL in bytecode.
    //
    // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
    // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
    //
    // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
    // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
    // "Another problem is that the specification allows aggressive
    // optimization of final fields. Within a thread, it is permissible to
    // reorder reads of a final field with those modifications of a final
    // field that do not take place in the constructor."
    //
    // A var or lazy val which is marked final still has meaning to the
    // scala compiler. The word final is heavily overloaded unfortunately;
    // for us it means "not overridable". At present you can't override
    // vars regardless; this may change.

    val finalFlag = (
           (sym.isFinal || isTopLevelModuleClass(sym))
        && !sym.enclClass.isTrait
        && !sym.isClassConstructor
        && (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are
      )

    // Primitives are "abstract final" to prohibit instantiation
    // without having to provide any implementations, but that is an
    // illegal combination of modifiers at the bytecode level so
    // suppress final if abstract is present.
    import asm.Opcodes._
    GenBCode.mkFlags(
      if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
      if ((sym.isDeferred && !sym.hasFlag(symtab.Flags.JAVA_DEFAULTMETHOD))|| sym.hasAbstractFlag) ACC_ABSTRACT else 0,
      if (sym.isTraitOrInterface) ACC_INTERFACE else 0,
      if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
      if (sym.isStaticMember) ACC_STATIC else 0,
      if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
      if (sym.isArtifact) ACC_SYNTHETIC else 0,
      if (sym.isClass && !sym.isTraitOrInterface) ACC_SUPER else 0,
      if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
      if (sym.isVarargsMethod) ACC_VARARGS else 0,
      if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
      if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0
    )
  }

  def javaFieldFlags(sym: Symbol) = {
    javaFlags(sym) | GenBCode.mkFlags(
      if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
      if (sym hasAnnotation VolatileAttr)  asm.Opcodes.ACC_VOLATILE  else 0,
      if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
    )
  }
}