@@ -833,6 +833,22 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
833833 case t =>
834834 foldOver(c, t)
835835
836+ /** Check that no fresh caps from the bounds of parameters or abstract types
837+ * are hidden in the result. See issue #24539
838+ */
839+ def checkNoTParamBounds (refsToCheck : Refs , descr : => String , pos : SrcPos ): Unit =
840+ for ref <- refsToCheck do
841+ ref match
842+ case ref : FreshCap =>
843+ ref.origin match
844+ case Origin .InDecl (sym) if sym.isAbstractOrParamType =>
845+ report.error(
846+ em """ Separation failure: $descr $ref, which appears in the bound of $sym.
847+ |This is not allowed. The $sym has to be returned explicitly in the result type. """ ,
848+ pos)
849+ case _ =>
850+ case _ =>
851+
836852 /** If `tpe` appears as a (result-) type of a definition, treat its
837853 * hidden set minus its explicitly declared footprint as consumed.
838854 * If `tpe` appears as an argument to a consume parameter, treat
@@ -847,8 +863,11 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
847863 // "see through them" when we look at hidden sets.
848864 then
849865 val refs = tpe.deepCaptureSet.elems
850- val toCheck = refs.transHiddenSet.directFootprint.nonPeaks.deduct(refs.directFootprint.nonPeaks)
851- checkConsumedRefs(toCheck, tpe, role, i " ${role.description} $tpe hides " , pos)
866+ val refsStar = refs.transHiddenSet
867+ val toCheck = refsStar.directFootprint.nonPeaks.deduct(refs.directFootprint.nonPeaks)
868+ def descr = i " ${role.description} $tpe hides "
869+ checkConsumedRefs(toCheck, tpe, role, descr, pos)
870+ checkNoTParamBounds(refsStar, descr, pos)
852871 case TypeRole .Argument (arg, _) =>
853872 if tpe.hasAnnotation(defn.ConsumeAnnot ) then
854873 val capts = spanCaptures(arg).directFootprint.nonPeaks
0 commit comments