@@ -594,12 +594,73 @@ object RefChecks {
594
594
checkNoAbstractDecls(bc.asClass.superClass)
595
595
}
596
596
597
+ // Check that every term member of this concrete class has a symbol that matches the member's type
598
+ // Member types are computed by intersecting the types of all members that have the same name
599
+ // and signature. But a member selection will pick one particular implementation, according to
600
+ // the rules of overriding and linearization. This method checks that the implementation has indeed
601
+ // a type that subsumes the full member type.
602
+ def checkMemberTypesOK () = {
603
+
604
+ // First compute all member names we need to check in `membersToCheck`.
605
+ // We do not check
606
+ // - types
607
+ // - synthetic members or bridges
608
+ // - members in other concrete classes, since these have been checked before
609
+ // (this is done for efficiency)
610
+ // - members in a prefix of inherited parents that all come from Java or Scala2
611
+ // (this is done to avoid false positives since Scala2's rules for checking are different)
612
+ val membersToCheck = new util.HashSet [Name ](4096 )
613
+ val seenClasses = new util.HashSet [Symbol ](256 )
614
+ def addDecls (cls : Symbol ): Unit =
615
+ if (! seenClasses.contains(cls)) {
616
+ seenClasses.addEntry(cls)
617
+ for (mbr <- cls.info.decls)
618
+ if (mbr.isTerm && ! mbr.is(Synthetic | Bridge ) && mbr.memberCanMatchInheritedSymbols &&
619
+ ! membersToCheck.contains(mbr.name))
620
+ membersToCheck.addEntry(mbr.name)
621
+ cls.info.parents.map(_.classSymbol)
622
+ .filter(_.is(AbstractOrTrait ))
623
+ .dropWhile(_.is(JavaDefined | Scala2x ))
624
+ .foreach(addDecls)
625
+ }
626
+ addDecls(clazz)
627
+
628
+ // For each member, check that the type of its symbol, as seen from `self`
629
+ // can override the info of this member
630
+ for (name <- membersToCheck) {
631
+ for (mbrd <- self.member(name).alternatives) {
632
+ val mbr = mbrd.symbol
633
+ val mbrType = mbr.info.asSeenFrom(self, mbr.owner)
634
+ if (! mbrType.overrides(mbrd.info, matchLoosely = true ))
635
+ ctx.errorOrMigrationWarning(
636
+ em """ ${mbr.showLocated} is not a legal implementation of ` $name' in $clazz
637
+ | its type $mbrType
638
+ | does not conform to ${mbrd.info}""" ,
639
+ (if (mbr.owner == clazz) mbr else clazz).pos)
640
+ }
641
+ }
642
+ }
643
+
644
+ /** Check that inheriting a case class does not constitute a variant refinement
645
+ * of a base type of the case class. It is because of this restriction that we
646
+ * can assume invariant refinement for case classes in `constrainPatternType`.
647
+ */
648
+ def checkCaseClassInheritanceInvariant () = {
649
+ for (caseCls <- clazz.info.baseClasses.tail.find(_.is(Case )))
650
+ for (baseCls <- caseCls.info.baseClasses.tail)
651
+ if (baseCls.typeParams.exists(_.paramVariance != 0 ))
652
+ for (problem <- variantInheritanceProblems(baseCls, caseCls, " non-variant" , " case " ))
653
+ ctx.errorOrMigrationWarning(problem(), clazz.pos)
654
+ }
597
655
checkNoAbstractMembers()
598
656
if (abstractErrors.isEmpty)
599
657
checkNoAbstractDecls(clazz)
600
658
601
659
if (abstractErrors.nonEmpty)
602
660
ctx.error(abstractErrorMessage, clazz.pos)
661
+
662
+ checkMemberTypesOK()
663
+ checkCaseClassInheritanceInvariant()
603
664
} else if (clazz.is(Trait ) && ! (clazz derivesFrom defn.AnyValClass )) {
604
665
// For non-AnyVal classes, prevent abstract methods in interfaces that override
605
666
// final members in Object; see #4431
@@ -613,6 +674,51 @@ object RefChecks {
613
674
}
614
675
}
615
676
677
+ if (! clazz.is(Trait )) {
678
+ // check that parameterized base classes and traits are typed in the same way as from the superclass
679
+ // I.e. say we have
680
+ //
681
+ // Sub extends Super extends* Base
682
+ //
683
+ // where `Base` has value parameters. Enforce that
684
+ //
685
+ // Sub.thisType.baseType(Base) =:= Sub.thisType.baseType(Super).baseType(Base)
686
+ //
687
+ // This is necessary because parameter values are determined directly or indirectly
688
+ // by `Super`. So we cannot pretend they have a different type when seen from `Sub`.
689
+ def checkParameterizedTraitsOK () = {
690
+ val mixins = clazz.mixins
691
+ for {
692
+ cls <- clazz.info.baseClasses.tail
693
+ if cls.paramAccessors.nonEmpty && ! mixins.contains(cls)
694
+ problem <- variantInheritanceProblems(cls, clazz.asClass.superClass, " parameterized" , " super" )
695
+ } ctx.error(problem(), clazz.pos)
696
+ }
697
+
698
+ checkParameterizedTraitsOK()
699
+ }
700
+
701
+ /** Check that `site` does not inherit conflicting generic instances of `baseCls`,
702
+ * when doing a direct base type or going via intermediate class `middle`. I.e, we require:
703
+ *
704
+ * site.baseType(baseCls) =:= site.baseType(middle).baseType(baseCls)
705
+ *
706
+ * Return an optional by name error message if this test fails.
707
+ */
708
+ def variantInheritanceProblems (
709
+ baseCls : Symbol , middle : Symbol , baseStr : String , middleStr : String ): Option [() => String ] = {
710
+ val superBT = self.baseType(middle)
711
+ val thisBT = self.baseType(baseCls)
712
+ val combinedBT = superBT.baseType(baseCls)
713
+ if (combinedBT =:= thisBT) None // ok
714
+ else
715
+ Some (() =>
716
+ em """ illegal inheritance: $clazz inherits conflicting instances of $baseStr base $baseCls.
717
+ |
718
+ | Direct basetype: $thisBT
719
+ | Basetype via $middleStr$middle: $combinedBT""" )
720
+ }
721
+
616
722
/* Returns whether there is a symbol declared in class `inclazz`
617
723
* (which must be different from `clazz`) whose name and type
618
724
* seen as a member of `class.thisType` matches `member`'s.
0 commit comments