@@ -2603,29 +2603,36 @@ class VarDeclUsageChecker : public ASTWalker {
2603
2603
// / An AST walker that determines the underlying type of an opaque return decl
2604
2604
// / from its associated function body.
2605
2605
class OpaqueUnderlyingTypeChecker : public ASTWalker {
2606
- using Candidate = std::pair<Expr *, SubstitutionMap>;
2606
+ using Candidate = std::tuple<Expr *, SubstitutionMap, /* isUnique=*/ bool >;
2607
+ using AvailabilityContext = IfStmt *;
2607
2608
2608
2609
ASTContext &Ctx;
2609
2610
AbstractFunctionDecl *Implementation;
2610
2611
OpaqueTypeDecl *OpaqueDecl;
2611
2612
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;
2614
2627
2615
2628
bool HasInvalidReturn = false ;
2616
2629
2617
2630
public:
2618
2631
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
+
2629
2636
void check () {
2630
2637
Body->walk (*this );
2631
2638
@@ -2642,95 +2649,242 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
2642
2649
return ;
2643
2650
}
2644
2651
2652
+ if (Candidates.size () == 1 ) {
2653
+ finalizeUnique (Candidates.front ().second );
2654
+ return ;
2655
+ }
2656
+
2645
2657
// Check whether all of the underlying type candidates match up.
2646
2658
// 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
- }
2666
2659
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 ;
2668
2714
break ;
2715
+ }
2669
2716
}
2670
- assert (mismatch.hasValue ());
2671
2717
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 ())
2677
2730
.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)
2684
2737
.highlight (opaqueRepr->getSourceRange ());
2685
- }
2738
+ }
2686
2739
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)));
2693
2744
}
2694
-
2745
+ }
2746
+
2747
+ bool isSelfReferencing (const Candidate &candidate) {
2748
+ auto substitutions = std::get<1 >(candidate);
2749
+
2695
2750
// The underlying type can't be defined recursively
2696
2751
// in terms of the opaque type itself.
2697
2752
auto opaqueTypeInContext = Implementation->mapTypeIntoContext (
2698
2753
OpaqueDecl->getDeclaredInterfaceType ());
2699
2754
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); });
2704
2758
2705
2759
if (isSelfReferencing) {
2706
- unsigned index = genericParam->getIndex ();
2707
- Ctx.Diags .diagnose (Candidates.front ().first ->getLoc (),
2760
+ Ctx.Diags .diagnose (std::get<0 >(candidate)->getLoc (),
2708
2761
diag::opaque_type_self_referential_underlying_type,
2709
- underlyingSubs. getReplacementTypes ()[index] );
2710
- return ;
2762
+ underlyingType );
2763
+ return true ;
2711
2764
}
2712
2765
}
2713
2766
2767
+ return false ;
2768
+ }
2769
+
2770
+ // A single unique underlying substitution.
2771
+ void finalizeUnique (const Candidate &candidate) {
2714
2772
// If we have one successful candidate, then save it as the underlying
2715
2773
// substitutions of the opaque decl.
2716
2774
OpaqueDecl->setUniqueUnderlyingTypeSubstitutions (
2717
- underlyingSubs .mapReplacementTypesOutOfContext ());
2775
+ std::get< 1 >(candidate) .mapReplacementTypesOutOfContext ());
2718
2776
}
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
+
2720
2816
std::pair<bool , Expr *> walkToExprPre (Expr *E) override {
2721
2817
if (auto underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(E)) {
2722
2818
auto key =
2723
2819
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 };
2727
2830
}
2831
+
2832
+ Candidates.push_back ({CurrentAvailability, candidate});
2728
2833
return {false , E};
2729
2834
}
2835
+
2730
2836
return {true , E};
2731
2837
}
2732
2838
2733
2839
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
+
2734
2888
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
2735
2889
if (RS->hasResult ()) {
2736
2890
auto resultTy = RS->getResult ()->getType ();
@@ -2743,7 +2897,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
2743
2897
2744
2898
return {true , S};
2745
2899
}
2746
-
2900
+
2747
2901
// Don't descend into nested decls.
2748
2902
bool walkToDeclPre (Decl *D) override {
2749
2903
return false ;
0 commit comments