@@ -10,7 +10,7 @@ import core.Flags._
1010import  core .Names .{DerivedName , Name , SimpleName , TypeName }
1111import  core .Symbols ._ 
1212import  core .TypeApplications .TypeParamInfo 
13- import  core .TypeErasure .{erasure , isUnboundedGeneric }
13+ import  core .TypeErasure .{erasedGlb ,  erasure , isUnboundedGeneric }
1414import  core .Types ._ 
1515import  core .classfile .ClassfileConstants 
1616import  ast .Trees ._ 
@@ -19,6 +19,7 @@ import TypeUtils._
1919import  java .lang .StringBuilder 
2020
2121import  scala .annotation .tailrec 
22+ import  scala .collection .mutable .ListBuffer 
2223
2324/**  Helper object to generate generic java signatures, as defined in
2425 *  the Java Virtual Machine Specification, §4.3.4 
@@ -65,18 +66,79 @@ object GenericSignatures {
6566
6667    def  boxedSig (tp : Type ):  Unit  =  jsig(tp.widenDealias, primitiveOK =  false )
6768
69+     /**  The signature of the upper-bound of a type parameter. 
70+      * 
71+      *  @pre none of the bounds are themselves type parameters. 
72+      *       TODO: Remove this restriction so we can support things like: 
73+      * 
74+      *           class Foo[A]: 
75+      *              def foo[S <: A & Object](...): ... 
76+      * 
77+      *        Which should emit a signature `S <: A`. See the handling 
78+      *        of `AndType` in `jsig` which already supports `def foo(x: A & Object)`. 
79+      */  
6880    def  boundsSig (bounds : List [Type ]):  Unit  =  {
69-       val  (isTrait, isClass) =  bounds partition (_.typeSymbol.is(Trait ))
70-       isClass match  {
71-         case  Nil     =>  builder.append(':' ) //  + boxedSig(ObjectTpe)
72-         case  x ::  _ =>  builder.append(':' ); boxedSig(x)
73-       }
74-       isTrait.foreach { tp => 
81+       val  (repr ::  _, others) =  splitIntersection(bounds)
82+       builder.append(':' )
83+ 
84+       //  According to the Java spec
85+       //  (https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.4),
86+       //  intersections erase to their first member and must start with a class.
87+       //  So, if our intersection erases to a trait, in theory we should emit
88+       //  just that trait in the generic signature even if the intersection type
89+       //  is composed of multiple traits. But in practice Scala 2 has always
90+       //  ignored this restriction as intersections of traits seem to be handled
91+       //  correctly by javac, we do the same here since type soundness seems
92+       //  more important than adhering to the spec.
93+       if  repr.classSymbol.is(Trait ) then 
94+         builder.append(':' )
95+         boxedSig(repr)
96+         //  If we wanted to be compliant with the spec, we would `return` here.
97+       else 
98+         boxedSig(repr)
99+       others.filter(_.classSymbol.is(Trait )).foreach { tp => 
75100        builder.append(':' )
76101        boxedSig(tp)
77102      }
78103    }
79104
105+     /**  The parents of this intersection where type parameters 
106+      *  that cannot appear in the signature have been replaced 
107+      *  by their upper-bound. 
108+      */  
109+     def  flattenedIntersection (tp : AndType )(using  Context ):  List [Type ] = 
110+       val  parents  =  ListBuffer [Type ]()
111+ 
112+       def  collect (parent : Type , parents : ListBuffer [Type ]):  Unit  =  parent.widenDealias match 
113+         case  AndType (tp1, tp2) => 
114+           collect(tp1, parents)
115+           collect(tp2, parents)
116+         case  parent : TypeRef  => 
117+           if  parent.symbol.isClass ||  isTypeParameterInSig(parent.symbol, sym0) then 
118+             parents +=  parent
119+           else 
120+             collect(parent.superType, parents)
121+         case  parent => 
122+           parents +=  parent
123+ 
124+       collect(tp, parents)
125+       parents.toList
126+     end  flattenedIntersection 
127+ 
128+     /**  Split the `parents` of an intersection into two subsets: 
129+      *  those whose individual erasure matches the overall erasure 
130+      *  of the intersection and the others. 
131+      */  
132+     def  splitIntersection (parents : List [Type ])(using  Context ):  (List [Type ], List [Type ]) = 
133+       val  erasedParents  =  parents.map(erasure)
134+       val  erasedCls  =  erasedGlb(erasedParents).classSymbol
135+       parents.zip(erasedParents)
136+         .partitionMap((parent, erasedParent) => 
137+           if  erasedParent.classSymbol eq erasedCls then 
138+             Left (parent)
139+           else 
140+             Right (parent))
141+ 
80142    def  paramSig (param : LambdaParam ):  Unit  =  {
81143      builder.append(sanitizeName(param.paramName))
82144      boundsSig(hiBounds(param.paramInfo.bounds))
@@ -263,8 +325,20 @@ object GenericSignatures {
263325          builder.append(')' )
264326          methodResultSig(restpe)
265327
266-         case  AndType (tp1, tp2) => 
267-           jsig(intersectionDominator(tp1 ::  tp2 ::  Nil ), primitiveOK =  primitiveOK)
328+         case  tp : AndType  => 
329+           //  Only intersections appearing as the upper-bound of a type parameter
330+           //  can be preserved in generic signatures and those are already
331+           //  handled by `boundsSig`, so here we fallback to picking a parent of
332+           //  the intersection to determine its overall signature. We must pick a
333+           //  parent whose erasure matches the erasure of the intersection
334+           //  because javac relies on the generic signature to determine the
335+           //  bytecode signature. Additionally, we prefer picking a type
336+           //  parameter since that will likely lead to a more precise type.
337+           val  parents  =  flattenedIntersection(tp)
338+           val  (reprParents, _) =  splitIntersection(parents)
339+           val  repr  = 
340+             reprParents.find(_.typeSymbol.is(TypeParam )).getOrElse(reprParents.head)
341+           jsig(repr, primitiveOK =  primitiveOK)
268342
269343        case  ci : ClassInfo  => 
270344          def  polyParamSig (tparams : List [TypeParamInfo ]):  Unit  = 
@@ -308,38 +382,6 @@ object GenericSignatures {
308382
309383  private  class  UnknownSig  extends  Exception 
310384
311-   /**  The intersection dominator (SLS 3.7) of a list of types is computed as follows. 
312-     * 
313-     *  - If the list contains one or more occurrences of scala.Array with 
314-     *    type parameters El1, El2, ... then the dominator is scala.Array with 
315-     *    type parameter of intersectionDominator(List(El1, El2, ...)).           <--- @PP: not yet in spec. 
316-     *  - Otherwise, the list is reduced to a subsequence containing only the 
317-     *    types that are not supertypes of other listed types (the span.) 
318-     *  - If the span is empty, the dominator is Object. 
319-     *  - If the span contains a class Tc which is not a trait and which is 
320-     *    not Object, the dominator is Tc.                                        <--- @PP: "which is not Object" not in spec. 
321-     *  - Otherwise, the dominator is the first element of the span. 
322-     */  
323-   private  def  intersectionDominator (parents : List [Type ])(using  Context ):  Type  = 
324-     if  (parents.isEmpty) defn.ObjectType 
325-     else  {
326-       val  psyms  =  parents map (_.typeSymbol)
327-       if  (psyms contains defn.ArrayClass )
328-         //  treat arrays specially
329-         defn.ArrayType .appliedTo(intersectionDominator(parents.filter(_.typeSymbol ==  defn.ArrayClass ).map(t =>  t.argInfos.head)))
330-       else  {
331-         //  implement new spec for erasure of refined types.
332-         def  isUnshadowed (psym : Symbol ) = 
333-           ! (psyms exists (qsym =>  (psym ne qsym) &&  (qsym isSubClass psym)))
334-         val  cs  =  parents.iterator.filter { p =>  //  isUnshadowed is a bit expensive, so try classes first
335-           val  psym  =  p.classSymbol
336-           psym.ensureCompleted()
337-           psym.isClass &&  ! psym.is(Trait ) &&  isUnshadowed(psym)
338-         }
339-         (if  (cs.hasNext) cs else  parents.iterator.filter(p =>  isUnshadowed(p.classSymbol))).next()
340-       }
341-     }
342- 
343385  /*  Drop redundant types (ones which are implemented by some other parent) from the immediate parents.
344386   * This is important on Android because there is otherwise an interface explosion. 
345387   */  
0 commit comments