@@ -824,6 +824,77 @@ class AvailabilityScopeBuilder : private ASTWalker {
824824 pushContext (fallthroughScope, parentBrace);
825825 }
826826
827+ AvailabilityQuery buildAvailabilityQuery (
828+ const SemanticAvailabilitySpec spec,
829+ const std::optional<SemanticAvailabilitySpec> &variantSpec,
830+ bool isUnavailability) {
831+ auto domain = spec.getDomain ();
832+
833+ // Variant availability specfications are only supported for platform
834+ // domains when compiling with a -target-variant.
835+ if (!Context.LangOpts .TargetVariant )
836+ ASSERT (!variantSpec);
837+
838+ auto runtimeRangeForSpec =
839+ [](const std::optional<SemanticAvailabilitySpec> &spec)
840+ -> std::optional<AvailabilityRange> {
841+ if (!spec || spec->isWildcard () || !spec->getDomain ().isVersioned ())
842+ return std::nullopt ;
843+
844+ return AvailabilityRange (spec->getRuntimeVersion ());
845+ };
846+
847+ auto primaryRange = runtimeRangeForSpec (spec);
848+ auto variantRange = runtimeRangeForSpec (variantSpec);
849+
850+ switch (domain.getKind ()) {
851+ case AvailabilityDomain::Kind::Embedded:
852+ case AvailabilityDomain::Kind::SwiftLanguage:
853+ case AvailabilityDomain::Kind::PackageDescription:
854+ // These domains don't support queries.
855+ llvm::report_fatal_error (" unsupported domain" );
856+
857+ case AvailabilityDomain::Kind::Universal:
858+ DEBUG_ASSERT (spec.isWildcard ());
859+
860+ // If all of the specs that matched are '*', then the query trivially
861+ // evaluates to "true" at compile time.
862+ if (!variantRange)
863+ return AvailabilityQuery::constant (domain, isUnavailability, true );
864+
865+ // Otherwise, generate a dynamic query for the variant spec. For example,
866+ // when compiling zippered for macOS, this should generate a query that
867+ // just checks the iOS version at runtime:
868+ //
869+ // if #available(iOS 18, *) { ... }
870+ //
871+ return AvailabilityQuery::dynamic (variantSpec->getDomain (),
872+ isUnavailability, primaryRange,
873+ variantRange);
874+
875+ case AvailabilityDomain::Kind::Platform:
876+ // Platform checks are always dynamic. The SIL optimizer is responsible
877+ // eliminating these checks when it can prove that they can never fail
878+ // (due to the deployment target). We can't perform that analysis here
879+ // because it may depend on inlining.
880+ return AvailabilityQuery::dynamic (domain, isUnavailability, primaryRange,
881+ variantRange);
882+ case AvailabilityDomain::Kind::Custom:
883+ auto customDomain = domain.getCustomDomain ();
884+ ASSERT (customDomain);
885+
886+ switch (customDomain->getKind ()) {
887+ case CustomAvailabilityDomain::Kind::Enabled:
888+ return AvailabilityQuery::constant (domain, isUnavailability, true );
889+ case CustomAvailabilityDomain::Kind::Disabled:
890+ return AvailabilityQuery::constant (domain, isUnavailability, false );
891+ case CustomAvailabilityDomain::Kind::Dynamic:
892+ return AvailabilityQuery::dynamic (domain, isUnavailability,
893+ primaryRange, variantRange);
894+ }
895+ }
896+ }
897+
827898 // / Build the availability scopes for a StmtCondition and return a pair of
828899 // / optional availability contexts, the first for the true branch and the
829900 // / second for the false branch. A value of `nullopt` for a given branch
@@ -982,23 +1053,18 @@ class AvailabilityScopeBuilder : private ASTWalker {
9821053 continue ;
9831054 }
9841055
985- auto runtimeQueryRange = runtimeQueryRangeForSpec (*spec);
986- query->setAvailableRange (runtimeQueryRange.getRawVersionRange ());
987-
9881056 // When compiling zippered for macCatalyst, we need to collect both
9891057 // a macOS version (the target version) and an iOS/macCatalyst version
9901058 // (the target-variant). These versions will both be passed to a runtime
9911059 // entrypoint that will check either the macOS version or the iOS
9921060 // version depending on the kind of process this code is loaded into.
993- if (Context.LangOpts .TargetVariant ) {
994- auto variantSpec =
995- bestActiveSpecForQuery (query, /* ForTargetVariant*/ true );
996- if (variantSpec) {
997- auto variantQueryRange = runtimeQueryRangeForSpec (*variantSpec);
998- query->setVariantAvailableRange (
999- variantQueryRange.getRawVersionRange ());
1000- }
1001- }
1061+ std::optional<SemanticAvailabilitySpec> variantSpec =
1062+ (Context.LangOpts .TargetVariant )
1063+ ? bestActiveSpecForQuery (query, /* ForTargetVariant*/ true )
1064+ : std::nullopt ;
1065+
1066+ query->setAvailabilityQuery (buildAvailabilityQuery (
1067+ *spec, variantSpec, query->isUnavailability ()));
10021068
10031069 // Wildcards are expected to be "useless". There may be other specs in
10041070 // this query that are useful when compiling for other platforms.
@@ -1164,24 +1230,6 @@ class AvailabilityScopeBuilder : private ASTWalker {
11641230 }
11651231 }
11661232
1167- // / Return the availability context for the given spec.
1168- AvailabilityRange runtimeQueryRangeForSpec (SemanticAvailabilitySpec spec) {
1169- if (spec.isWildcard ())
1170- return AvailabilityRange::alwaysAvailable ();
1171-
1172- auto domain = spec.getDomain ();
1173- if (domain.isVersioned ())
1174- return AvailabilityRange (spec.getRuntimeVersion ());
1175-
1176- // If it's not a versioned domain, we must be querying whether a domain is
1177- // present or absent.
1178- if (domain.isCustom () && domain.getCustomDomain ()->getKind () ==
1179- CustomAvailabilityDomain::Kind::Disabled)
1180- return AvailabilityRange::neverAvailable ();
1181-
1182- return AvailabilityRange::alwaysAvailable ();
1183- }
1184-
11851233 // / For the given spec, returns a pair of availability ranges. The first range
11861234 // / is for the if/then flow and the second is for the if/else flow.
11871235 std::pair<std::optional<AvailabilityRange>, std::optional<AvailabilityRange>>
0 commit comments