@@ -464,120 +464,6 @@ take into account field default values, and this change is necessary to make it
464464use them when the given ` p: Product ` has a smaller ` productArity ` than the current
465465` CaseClass ` implementation
466466
467- ### Abstract Methods
468-
469- Apart from ` final ` methods, ` @unroll ` also supports purely abstract methods. Consider
470- the following example with a trait ` Unrolled ` and an implementation ` UnrolledObj ` :
471-
472- ``` scala
473- trait Unrolled { // version 3
474- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String
475- }
476- ```
477- ``` scala
478- object UnrolledObj extends Unrolled { // version 3
479- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ) = s + n + b
480- }
481- ```
482-
483- This unrolls to:
484- ``` scala
485- trait Unrolled { // version 3
486- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String = foo(s, n, b)
487- def foo (s : String , n : Int , b : Boolean ): String = foo(s, n)
488- def foo (s : String , n : Int ): String
489- }
490- ```
491- ``` scala
492- object UnrolledObj extends Unrolled { // version 3
493- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ) = s + n + b + l
494- def foo (s : String , n : Int , b : Boolean ) = foo(s, n, b, 0 )
495- def foo (s : String , n : Int ) = foo(s, n, true )
496- }
497- ```
498-
499- Note that both the abstract methods from ` trait Unrolled ` and the concrete methods
500- from ` object UnrolledObj ` generate forwarders when ` @unroll ` ed, but the forwarders
501- are generated _ in opposite directions_ ! Unrolled concrete methods forward from longer
502- parameter lists to shorter parameter lists, while unrolled abstract methods forward
503- from shorter parameter lists to longer parameter lists. For example, we may have a
504- version of ` object UnrolledObj ` that was compiled against an earlier version of ` trait Unrolled ` :
505-
506-
507- ``` scala
508- object UnrolledObj extends Unrolled { // version 2
509- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true ) = s + n + b
510- def foo (s : String , n : Int ) = foo(s, n, true )
511- }
512- ```
513-
514- But further downstream code calling ` .foo ` on ` UnrolledObj ` may expect any of the following signatures,
515- depending on what version of ` Unrolled ` and ` UnrolledObj ` it was compiled against:
516-
517- ``` scala
518- UnrolledObj .foo(String , Int )
519- UnrolledObj .foo(String , Int , Boolean )
520- UnrolledObj .foo(String , Int , Boolean , Long )
521- ```
522-
523- Because such downstream code cannot know which version of ` Unrolled ` that ` UnrolledObj `
524- was compiled against, we need to ensure all such calls find their way to the correct
525- implementation of ` def foo ` , which may be at any of the above signatures. This "double
526- forwarding" strategy ensures that regardless of _ which_ version of ` .foo ` gets called,
527- it ends up eventually forwarding to the actual implementation of ` foo ` , with
528- the correct combination of passed arguments and default arguments
529-
530- ``` scala
531- UnrolledObj .foo(String , Int ) // forwards to UnrolledObj.foo(String, Int, Boolean)
532- UnrolledObj .foo(String , Int , Boolean ) // actual implementation
533- UnrolledObj .foo(String , Int , Boolean , Long ) // forwards to UnrolledObj.foo(String, Int, Boolean)
534- ```
535-
536- As is the case for ` @unroll ` ed methods on ` trait ` s and ` class ` es, ` @unroll ` ed
537- implementations of an abtract method must be final.
538-
539- #### Are Reverse Forwarders Really Necessary?
540-
541- This "double forwarding" strategy is not strictly necessary to support
542- [ Backwards Compatibility] ( #backwards-compatibility ) : the "reverse" forwarders
543- generated for abstract methods are only necessary when a downstream callsite
544- of ` UnrolledObj.foo ` is compiled against a newer version of the original
545- ` trait Unrolled ` than the ` object UnrolledObj ` was, as shown below:
546-
547- ``` scala
548- trait Unrolled { // version 3
549- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String = foo(s, n, b)
550- // generated
551- def foo (s : String , n : Int , b : Boolean ): String = foo(s, n)
552- def foo (s : String , n : Int ): String
553- }
554- ```
555- ``` scala
556- object UnrolledObj extends Unrolled { // version 2
557- def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true ) = s + n + b
558- // generated
559- def foo (s : String , n : Int ) = foo(s, n, true )
560- }
561- ```
562- ``` scala
563- // version 3
564- UnrolledObj .foo(" hello" , 123 , true , 456L )
565- ```
566-
567- If we did not have the reverse forwarder from ` foo(String, Int, Boolean, Long) ` to
568- ` foo(String, Int, Boolean) ` , this call would fail at runtime with an ` AbstractMethodError ` .
569- It also will get caught by MiMa as a ` ReversedMissingMethodProblem ` .
570-
571- This configuration of version is not allowed given our definition of backwards compatibility:
572- that definition assumes that ` Unrolled ` must be of a greater or equal version than ` UnrolledObj ` ,
573- which itself must be of a greater or equal version than the final call to ` UnrolledObj.foo ` . However,
574- the reverse forwarders are needed to fulfill our requirement
575- [ All Overrides Are Equivalent] ( #all-overrides-are-equivalent ) :
576- looking at ` trait Unrolled // version 3 ` and ` object UnrolledObj // version 2 ` in isolation,
577- we find that without the reverse forwarders the signature ` foo(String, Int, Boolean, Long) `
578- is defined but not implemented. Such an un-implemented abstract method is something
579- we want to avoid, even if our artifact version constraints mean it should technically
580- never get called.
581467
582468### Hiding Generated Forwarder Methods
583469
@@ -811,6 +697,121 @@ evolution. It does so with the same Scala-level syntax and semantics, with the s
811697and limitations that common schema/API/protocol-evolution best-practices have in the broader
812698software engineering community.
813699
700+ ### Abstract Methods
701+
702+ Apart from ` final ` methods, ` @unroll ` also supports purely abstract methods. Consider
703+ the following example with a trait ` Unrolled ` and an implementation ` UnrolledObj ` :
704+
705+ ``` scala
706+ trait Unrolled { // version 3
707+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String
708+ }
709+ ```
710+ ``` scala
711+ object UnrolledObj extends Unrolled { // version 3
712+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ) = s + n + b
713+ }
714+ ```
715+
716+ This unrolls to:
717+ ``` scala
718+ trait Unrolled { // version 3
719+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String = foo(s, n, b)
720+ def foo (s : String , n : Int , b : Boolean ): String = foo(s, n)
721+ def foo (s : String , n : Int ): String
722+ }
723+ ```
724+ ``` scala
725+ object UnrolledObj extends Unrolled { // version 3
726+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ) = s + n + b + l
727+ def foo (s : String , n : Int , b : Boolean ) = foo(s, n, b, 0 )
728+ def foo (s : String , n : Int ) = foo(s, n, true )
729+ }
730+ ```
731+
732+ Note that both the abstract methods from ` trait Unrolled ` and the concrete methods
733+ from ` object UnrolledObj ` generate forwarders when ` @unroll ` ed, but the forwarders
734+ are generated _ in opposite directions_ ! Unrolled concrete methods forward from longer
735+ parameter lists to shorter parameter lists, while unrolled abstract methods forward
736+ from shorter parameter lists to longer parameter lists. For example, we may have a
737+ version of ` object UnrolledObj ` that was compiled against an earlier version of ` trait Unrolled ` :
738+
739+
740+ ``` scala
741+ object UnrolledObj extends Unrolled { // version 2
742+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true ) = s + n + b
743+ def foo (s : String , n : Int ) = foo(s, n, true )
744+ }
745+ ```
746+
747+ But further downstream code calling ` .foo ` on ` UnrolledObj ` may expect any of the following signatures,
748+ depending on what version of ` Unrolled ` and ` UnrolledObj ` it was compiled against:
749+
750+ ``` scala
751+ UnrolledObj .foo(String , Int )
752+ UnrolledObj .foo(String , Int , Boolean )
753+ UnrolledObj .foo(String , Int , Boolean , Long )
754+ ```
755+
756+ Because such downstream code cannot know which version of ` Unrolled ` that ` UnrolledObj `
757+ was compiled against, we need to ensure all such calls find their way to the correct
758+ implementation of ` def foo ` , which may be at any of the above signatures. This "double
759+ forwarding" strategy ensures that regardless of _ which_ version of ` .foo ` gets called,
760+ it ends up eventually forwarding to the actual implementation of ` foo ` , with
761+ the correct combination of passed arguments and default arguments
762+
763+ ``` scala
764+ UnrolledObj .foo(String , Int ) // forwards to UnrolledObj.foo(String, Int, Boolean)
765+ UnrolledObj .foo(String , Int , Boolean ) // actual implementation
766+ UnrolledObj .foo(String , Int , Boolean , Long ) // forwards to UnrolledObj.foo(String, Int, Boolean)
767+ ```
768+
769+ As is the case for ` @unroll ` ed methods on ` trait ` s and ` class ` es, ` @unroll ` ed
770+ implementations of an abtract method must be final.
771+
772+ #### Are Reverse Forwarders Really Necessary?
773+
774+ This "double forwarding" strategy is not strictly necessary to support
775+ [ Backwards Compatibility] ( #backwards-compatibility ) : the "reverse" forwarders
776+ generated for abstract methods are only necessary when a downstream callsite
777+ of ` UnrolledObj.foo ` is compiled against a newer version of the original
778+ ` trait Unrolled ` than the ` object UnrolledObj ` was, as shown below:
779+
780+ ``` scala
781+ trait Unrolled { // version 3
782+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true , @ unroll l : Long = 0 ): String = foo(s, n, b)
783+ // generated
784+ def foo (s : String , n : Int , b : Boolean ): String = foo(s, n)
785+ def foo (s : String , n : Int ): String
786+ }
787+ ```
788+ ``` scala
789+ object UnrolledObj extends Unrolled { // version 2
790+ def foo (s : String , n : Int = 1 , @ unroll b : Boolean = true ) = s + n + b
791+ // generated
792+ def foo (s : String , n : Int ) = foo(s, n, true )
793+ }
794+ ```
795+ ``` scala
796+ // version 3
797+ UnrolledObj .foo(" hello" , 123 , true , 456L )
798+ ```
799+
800+ If we did not have the reverse forwarder from ` foo(String, Int, Boolean, Long) ` to
801+ ` foo(String, Int, Boolean) ` , this call would fail at runtime with an ` AbstractMethodError ` .
802+ It also will get caught by MiMa as a ` ReversedMissingMethodProblem ` .
803+
804+ This configuration of version is not allowed given our definition of backwards compatibility:
805+ that definition assumes that ` Unrolled ` must be of a greater or equal version than ` UnrolledObj ` ,
806+ which itself must be of a greater or equal version than the final call to ` UnrolledObj.foo ` . However,
807+ the reverse forwarders are needed to fulfill our requirement
808+ [ All Overrides Are Equivalent] ( #all-overrides-are-equivalent ) :
809+ looking at ` trait Unrolled // version 3 ` and ` object UnrolledObj // version 2 ` in isolation,
810+ we find that without the reverse forwarders the signature ` foo(String, Int, Boolean, Long) `
811+ is defined but not implemented. Such an un-implemented abstract method is something
812+ we want to avoid, even if our artifact version constraints mean it should technically
813+ never get called.
814+
814815## Minor Alternatives:
815816
816817
0 commit comments