@@ -2603,29 +2603,36 @@ class VarDeclUsageChecker : public ASTWalker {
26032603// / An AST walker that determines the underlying type of an opaque return decl
26042604// / from its associated function body.
26052605class OpaqueUnderlyingTypeChecker : public ASTWalker {
2606- using Candidate = std::pair<Expr *, SubstitutionMap>;
2606+ using Candidate = std::tuple<Expr *, SubstitutionMap, /* isUnique=*/ bool >;
2607+ using AvailabilityContext = IfStmt *;
26072608
26082609 ASTContext &Ctx;
26092610 AbstractFunctionDecl *Implementation;
26102611 OpaqueTypeDecl *OpaqueDecl;
26112612 BraceStmt *Body;
2612- SmallVector<Candidate, 4 > Candidates;
2613- SmallPtrSet<const void *, 4 > KnownCandidates;
2613+
2614+ // / A set of all candidates with unique signatures.
2615+ SmallPtrSet<const void *, 4 > UniqueSignatures;
2616+
2617+ // / Represents a current availability context. `nullptr` means that
2618+ // / there are no restrictions.
2619+ AvailabilityContext CurrentAvailability = nullptr ;
2620+
2621+ // / All of the candidates together with their availability.
2622+ // /
2623+ // / If a candidate is found in non-`if #available` context or
2624+ // / `if #available` has other dynamic conditions, it covers 'all'
2625+ // / versions and the context is set to `nullptr`.
2626+ SmallVector<std::pair<AvailabilityContext, Candidate>, 4 > Candidates;
26142627
26152628 bool HasInvalidReturn = false ;
26162629
26172630public:
26182631 OpaqueUnderlyingTypeChecker (AbstractFunctionDecl *Implementation,
2619- OpaqueTypeDecl *OpaqueDecl,
2620- BraceStmt *Body)
2621- : Ctx(Implementation->getASTContext ()),
2622- Implementation(Implementation),
2623- OpaqueDecl(OpaqueDecl),
2624- Body(Body)
2625- {
2626-
2627- }
2628-
2632+ OpaqueTypeDecl *OpaqueDecl, BraceStmt *Body)
2633+ : Ctx(Implementation->getASTContext ()), Implementation(Implementation),
2634+ OpaqueDecl(OpaqueDecl), Body(Body) {}
2635+
26292636 void check () {
26302637 Body->walk (*this );
26312638
@@ -2642,95 +2649,242 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
26422649 return ;
26432650 }
26442651
2652+ if (Candidates.size () == 1 ) {
2653+ finalizeUnique (Candidates.front ().second );
2654+ return ;
2655+ }
2656+
26452657 // Check whether all of the underlying type candidates match up.
26462658 // TODO [OPAQUE SUPPORT]: diagnose multiple opaque types
2647- SubstitutionMap underlyingSubs = Candidates.front ().second ;
2648- if (Candidates.size () > 1 ) {
2649- Optional<std::pair<unsigned , GenericTypeParamType *>> mismatch;
2650-
2651- auto opaqueParams = OpaqueDecl->getOpaqueGenericParams ();
2652- for (auto index : indices (opaqueParams)) {
2653- auto *genericParam = opaqueParams[index];
2654-
2655- Type underlyingType = Type (genericParam).subst (underlyingSubs);
2656- bool found = false ;
2657- for (const auto &candidate : Candidates) {
2658- Type otherType = Type (genericParam).subst (candidate.second );
2659-
2660- if (!underlyingType->isEqual (otherType)) {
2661- mismatch.emplace (index, genericParam);
2662- found = true ;
2663- break ;
2664- }
2665- }
26662659
2667- if (found)
2660+ // There is a single unique signature, which means that all returns
2661+ // matched.
2662+ if (llvm::count_if (Candidates, [](const auto &entry) {
2663+ const auto &candidate = entry.second ;
2664+ return std::get<2 >(candidate); // isUnique field.
2665+ }) == 1 ) {
2666+ finalizeUnique (Candidates.front ().second );
2667+ return ;
2668+ }
2669+
2670+ SmallVector<Candidate, 4 > universalyUniqueCandidates;
2671+
2672+ for (const auto &entry : Candidates) {
2673+ AvailabilityContext availability = entry.first ;
2674+ const auto &candidate = entry.second ;
2675+
2676+ // Unique candidate without availability context.
2677+ if (!availability && std::get<2 >(candidate))
2678+ universalyUniqueCandidates.push_back (candidate);
2679+ }
2680+
2681+ // TODO(diagnostics): Need a tailored diagnostic for this case.
2682+ if (universalyUniqueCandidates.empty ()) {
2683+ Implementation->diagnose (diag::opaque_type_no_underlying_type_candidates);
2684+ return ;
2685+ }
2686+
2687+ // If there is a single universally available unique candidate
2688+ // the underlying type would have to be determined at runtime
2689+ // based on the results of availability checks.
2690+ if (universalyUniqueCandidates.size () == 1 ) {
2691+ finalizeOpaque (universalyUniqueCandidates.front ());
2692+ return ;
2693+ }
2694+
2695+ // A list of all mismatches discovered across all candidates.
2696+ // If there are any mismatches in availability contexts, they
2697+ // are not diagnosed but propagated to the declaration.
2698+ Optional<std::pair<unsigned , GenericTypeParamType *>> mismatch;
2699+
2700+ auto opaqueParams = OpaqueDecl->getOpaqueGenericParams ();
2701+ SubstitutionMap underlyingSubs = std::get<1 >(Candidates.front ().second );
2702+
2703+ for (auto index : indices (opaqueParams)) {
2704+ auto *genericParam = opaqueParams[index];
2705+
2706+ Type underlyingType = Type (genericParam).subst (underlyingSubs);
2707+ bool found = false ;
2708+ for (const auto &candidate : universalyUniqueCandidates) {
2709+ Type otherType = Type (genericParam).subst (std::get<1 >(candidate));
2710+
2711+ if (!underlyingType->isEqual (otherType)) {
2712+ mismatch.emplace (index, genericParam);
2713+ found = true ;
26682714 break ;
2715+ }
26692716 }
2670- assert (mismatch.hasValue ());
26712717
2672- if (auto genericParam =
2673- OpaqueDecl->getExplicitGenericParam (mismatch->first )) {
2674- Implementation->diagnose (
2675- diag::opaque_type_mismatched_underlying_type_candidates_named,
2676- genericParam->getName ())
2718+ if (found)
2719+ break ;
2720+ }
2721+
2722+ assert (mismatch.hasValue ());
2723+
2724+ if (auto genericParam =
2725+ OpaqueDecl->getExplicitGenericParam (mismatch->first )) {
2726+ Implementation
2727+ ->diagnose (
2728+ diag::opaque_type_mismatched_underlying_type_candidates_named,
2729+ genericParam->getName ())
26772730 .highlight (genericParam->getLoc ());
2678- } else {
2679- TypeRepr *opaqueRepr =
2680- OpaqueDecl->getOpaqueReturnTypeReprs ()[mismatch->first ];
2681- Implementation-> diagnose (
2682- diag::opaque_type_mismatched_underlying_type_candidates,
2683- opaqueRepr)
2731+ } else {
2732+ TypeRepr *opaqueRepr =
2733+ OpaqueDecl->getOpaqueReturnTypeReprs ()[mismatch->first ];
2734+ Implementation
2735+ -> diagnose ( diag::opaque_type_mismatched_underlying_type_candidates,
2736+ opaqueRepr)
26842737 .highlight (opaqueRepr->getSourceRange ());
2685- }
2738+ }
26862739
2687- for (auto candidate : Candidates) {
2688- Ctx.Diags .diagnose (candidate.first ->getLoc (),
2689- diag::opaque_type_underlying_type_candidate_here,
2690- Type (mismatch->second ).subst (candidate.second ));
2691- }
2692- return ;
2740+ for (const auto &candidate : universalyUniqueCandidates) {
2741+ Ctx.Diags .diagnose (std::get<0 >(candidate)->getLoc (),
2742+ diag::opaque_type_underlying_type_candidate_here,
2743+ Type (mismatch->second ).subst (std::get<1 >(candidate)));
26932744 }
2694-
2745+ }
2746+
2747+ bool isSelfReferencing (const Candidate &candidate) {
2748+ auto substitutions = std::get<1 >(candidate);
2749+
26952750 // The underlying type can't be defined recursively
26962751 // in terms of the opaque type itself.
26972752 auto opaqueTypeInContext = Implementation->mapTypeIntoContext (
26982753 OpaqueDecl->getDeclaredInterfaceType ());
26992754 for (auto genericParam : OpaqueDecl->getOpaqueGenericParams ()) {
2700- auto underlyingType = Type (genericParam).subst (underlyingSubs);
2701- auto isSelfReferencing = underlyingType.findIf ([&](Type t) -> bool {
2702- return t->isEqual (opaqueTypeInContext);
2703- });
2755+ auto underlyingType = Type (genericParam).subst (substitutions);
2756+ auto isSelfReferencing = underlyingType.findIf (
2757+ [&](Type t) -> bool { return t->isEqual (opaqueTypeInContext); });
27042758
27052759 if (isSelfReferencing) {
2706- unsigned index = genericParam->getIndex ();
2707- Ctx.Diags .diagnose (Candidates.front ().first ->getLoc (),
2760+ Ctx.Diags .diagnose (std::get<0 >(candidate)->getLoc (),
27082761 diag::opaque_type_self_referential_underlying_type,
2709- underlyingSubs. getReplacementTypes ()[index] );
2710- return ;
2762+ underlyingType );
2763+ return true ;
27112764 }
27122765 }
27132766
2767+ return false ;
2768+ }
2769+
2770+ // A single unique underlying substitution.
2771+ void finalizeUnique (const Candidate &candidate) {
27142772 // If we have one successful candidate, then save it as the underlying
27152773 // substitutions of the opaque decl.
27162774 OpaqueDecl->setUniqueUnderlyingTypeSubstitutions (
2717- underlyingSubs .mapReplacementTypesOutOfContext ());
2775+ std::get< 1 >(candidate) .mapReplacementTypesOutOfContext ());
27182776 }
2719-
2777+
2778+ // There is no clear winner here since there are candidates within
2779+ // limited availability contexts.
2780+ void finalizeOpaque (const Candidate &universallyAvailable) {
2781+ SmallVector<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *, 4 >
2782+ conditionalSubstitutions;
2783+
2784+ for (const auto &entry : Candidates) {
2785+ auto availabilityContext = entry.first ;
2786+ const auto &candidate = entry.second ;
2787+
2788+ if (!availabilityContext)
2789+ continue ;
2790+
2791+ SmallVector<VersionRange, 4 > conditions;
2792+
2793+ llvm::transform (availabilityContext->getCond (),
2794+ std::back_inserter (conditions),
2795+ [&](const StmtConditionElement &elt) {
2796+ return elt.getAvailability ()->getAvailableRange ();
2797+ });
2798+
2799+ conditionalSubstitutions.push_back (
2800+ OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get (
2801+ Ctx, conditions,
2802+ std::get<1 >(candidate).mapReplacementTypesOutOfContext ()));
2803+ }
2804+
2805+ // Add universally available choice as the last one.
2806+ conditionalSubstitutions.push_back (
2807+ OpaqueTypeDecl::ConditionallyAvailableSubstitutions::get (
2808+ Ctx, {VersionRange::empty ()},
2809+ std::get<1 >(universallyAvailable)
2810+ .mapReplacementTypesOutOfContext ()));
2811+
2812+ OpaqueDecl->setConditionallyAvailableSubstitutions (
2813+ conditionalSubstitutions);
2814+ }
2815+
27202816 std::pair<bool , Expr *> walkToExprPre (Expr *E) override {
27212817 if (auto underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(E)) {
27222818 auto key =
27232819 underlyingToOpaque->substitutions .getCanonical ().getOpaqueValue ();
2724- if (KnownCandidates.insert (key).second ) {
2725- Candidates.push_back (std::make_pair (underlyingToOpaque->getSubExpr (),
2726- underlyingToOpaque->substitutions ));
2820+
2821+ auto isUnique = UniqueSignatures.insert (key).second ;
2822+
2823+ auto candidate =
2824+ std::make_tuple (underlyingToOpaque->getSubExpr (),
2825+ underlyingToOpaque->substitutions , isUnique);
2826+
2827+ if (isSelfReferencing (candidate)) {
2828+ HasInvalidReturn = true ;
2829+ return {false , nullptr };
27272830 }
2831+
2832+ Candidates.push_back ({CurrentAvailability, candidate});
27282833 return {false , E};
27292834 }
2835+
27302836 return {true , E};
27312837 }
27322838
27332839 std::pair<bool , Stmt *> walkToStmtPre (Stmt *S) override {
2840+ if (auto *If = dyn_cast<IfStmt>(S)) {
2841+ if (Parent.getAsStmt () != Body) {
2842+ // If this is not a top-level `if`, let's drop
2843+ // contextual information that has been set previously.
2844+ CurrentAvailability = nullptr ;
2845+ return {true , S};
2846+ }
2847+
2848+ // If this is `if #available` statement with no other dynamic
2849+ // conditions, let's check if it returns opaque type directly.
2850+ if (llvm::all_of (If->getCond (), [&](const auto &condition) {
2851+ return condition.getKind () == StmtConditionElement::CK_Availability;
2852+ })) {
2853+ // Check return statement directly with availability context set.
2854+ if (auto *Then = dyn_cast<BraceStmt>(If->getThenStmt ())) {
2855+ llvm::SaveAndRestore<ParentTy> parent (Parent, Then);
2856+
2857+ for (auto element : Then->getElements ()) {
2858+ auto *Return = getAsStmt<ReturnStmt>(element);
2859+
2860+ // If this is not a direct return statement, walk into it
2861+ // without setting contextual availability because we want
2862+ // to find all `return`s.
2863+ if (!(Return && Return->hasResult ())) {
2864+ element.walk (*this );
2865+ continue ;
2866+ }
2867+
2868+ // Note that we are about to walk into a return statement
2869+ // that is located in a `if #available` without any other
2870+ // conditions.
2871+ llvm::SaveAndRestore<AvailabilityContext> context (
2872+ CurrentAvailability, If);
2873+
2874+ Return->getResult ()->walk (*this );
2875+ }
2876+ }
2877+
2878+ // Walk the else branch directly as well.
2879+ if (auto *Else = If->getElseStmt ()) {
2880+ llvm::SaveAndRestore<ParentTy> parent (Parent, If);
2881+ Else->walk (*this );
2882+ }
2883+
2884+ return {false , S};
2885+ }
2886+ }
2887+
27342888 if (auto *RS = dyn_cast<ReturnStmt>(S)) {
27352889 if (RS->hasResult ()) {
27362890 auto resultTy = RS->getResult ()->getType ();
@@ -2743,7 +2897,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
27432897
27442898 return {true , S};
27452899 }
2746-
2900+
27472901 // Don't descend into nested decls.
27482902 bool walkToDeclPre (Decl *D) override {
27492903 return false ;
0 commit comments