Skip to content

Availability and backward deployment for variadic generics [5.9] #65926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,10 @@ class ASTContext final {
/// needed to place array buffers into constant data sections.
AvailabilityContext getImmortalRefCountSymbolsAvailability();

/// Get the runtime availability of runtime functions for
/// variadic generic types.
AvailabilityContext getVariadicGenericTypeAvailability();

/// Get the runtime availability of features introduced in the Swift 5.2
/// compiler for the target platform.
AvailabilityContext getSwift52Availability();
Expand Down
8 changes: 6 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2160,10 +2160,10 @@ NOTE(decl_import_via_here,none,

// Opaque return types
ERROR(opaque_type_invalid_constraint,none,
"an 'opaque' type must specify only 'Any', 'AnyObject', protocols, "
"a 'some' type must specify only 'Any', 'AnyObject', protocols, "
"and/or a base class", ())
NOTE(opaque_of_optional_rewrite,none,
"did you mean to write an optional of an 'opaque' type?", ())
"did you mean to write an optional of an 'some' type?", ())
ERROR(inferred_opaque_type,none,
"property definition has inferred type %0, involving the 'some' "
"return type of another declaration", (Type))
Expand Down Expand Up @@ -6143,6 +6143,10 @@ ERROR(availability_parameterized_protocol_only_version_newer, none,
"%0 %1 or newer",
(StringRef, llvm::VersionTuple))

ERROR(availability_variadic_type_only_version_newer, none,
"parameter packs in generic types are only available in %0 %1 or newer",
(StringRef, llvm::VersionTuple))

NOTE(availability_guard_with_version_check, none,
"add 'if #available' version check", ())

Expand Down
13 changes: 7 additions & 6 deletions include/swift/Frontend/BackDeploymentLibs.def
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@
//===----------------------------------------------------------------------===//

#ifndef BACK_DEPLOYMENT_LIB
# error "Must define BACK_DEPLOYMENT_LIB(Version, Filter, Library)"
# error "Must define BACK_DEPLOYMENT_LIB(Version, Filter, Library, ForceLoad)"
#endif

BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50")
BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51")
BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements")
BACK_DEPLOYMENT_LIB((5, 4), all, "swiftCompatibilityConcurrency")
BACK_DEPLOYMENT_LIB((5, 6), all, "swiftCompatibility56")
BACK_DEPLOYMENT_LIB((5, 0), all, "swiftCompatibility50", true)
BACK_DEPLOYMENT_LIB((5, 1), all, "swiftCompatibility51", true)
BACK_DEPLOYMENT_LIB((5, 0), executable, "swiftCompatibilityDynamicReplacements", true)
BACK_DEPLOYMENT_LIB((5, 4), all, "swiftCompatibilityConcurrency", true)
BACK_DEPLOYMENT_LIB((5, 6), all, "swiftCompatibility56", true)
BACK_DEPLOYMENT_LIB((5, 8), all, "swiftCompatibilityPacks", false)

#undef BACK_DEPLOYMENT_LIB
5 changes: 5 additions & 0 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,11 @@ ASTContext::getImmortalRefCountSymbolsAvailability() {
return getSwiftFutureAvailability();
}

AvailabilityContext
ASTContext::getVariadicGenericTypeAvailability() {
return getSwift59Availability();
}

AvailabilityContext ASTContext::getSwift52Availability() {
auto target = LangOpts.Target;

Expand Down
27 changes: 16 additions & 11 deletions lib/Basic/TargetInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ using namespace swift;
/// Print information about a
static void printCompatibilityLibrary(
llvm::VersionTuple runtimeVersion, llvm::VersionTuple maxVersion,
StringRef filter, StringRef libraryName, bool &printedAny,
llvm::raw_ostream &out) {
StringRef filter, StringRef libraryName, bool forceLoad,
bool &printedAny, llvm::raw_ostream &out) {
if (runtimeVersion > maxVersion)
return;

Expand All @@ -33,16 +33,21 @@ static void printCompatibilityLibrary(
}

out << "\n";
out << " {\n";
out << " {";

out << " \"libraryName\": \"";
out << "\n \"libraryName\": \"";
swift::writeEscaped(libraryName, out);
out << "\",\n";
out << "\",";

out << " \"filter\": \"";
out << "\n \"filter\": \"";
swift::writeEscaped(filter, out);
out << "\"\n";
out << " }";
out << "\"";

if (!forceLoad) {
out << ",\n \"forceLoad\": false";
}

out << "\n }";

printedAny = true;
}
Expand Down Expand Up @@ -132,10 +137,10 @@ void targetinfo::printTripleInfo(const llvm::Triple &triple,
// Compatibility libraries that need to be linked.
out << " \"compatibilityLibraries\": [";
bool printedAnyCompatibilityLibrary = false;
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \
printCompatibilityLibrary( \
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName, ForceLoad) \
printCompatibilityLibrary( \
*runtimeVersion, llvm::VersionTuple Version, #Filter, LibraryName, \
printedAnyCompatibilityLibrary, out);
ForceLoad, printedAnyCompatibilityLibrary, out);
#include "swift/Frontend/BackDeploymentLibs.def"

if (printedAnyCompatibilityLibrary) {
Expand Down
15 changes: 10 additions & 5 deletions lib/Driver/DarwinToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments,
runtimeCompatibilityVersion = llvm::VersionTuple(5, 5);
} else if (value.equals("5.6")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 6);
} else if (value.equals("5.8")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 8);
} else if (value.equals("none")) {
runtimeCompatibilityVersion = None;
} else {
Expand All @@ -419,7 +421,8 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments,
if (runtimeCompatibilityVersion) {
auto addBackDeployLib = [&](llvm::VersionTuple version,
BackDeployLibFilter filter,
StringRef libraryName) {
StringRef libraryName,
bool forceLoad) {
if (*runtimeCompatibilityVersion > version)
return;

Expand All @@ -431,14 +434,16 @@ toolchains::Darwin::addArgsToLinkStdlib(ArgStringList &Arguments,
llvm::sys::path::append(BackDeployLib, "lib" + libraryName + ".a");

if (llvm::sys::fs::exists(BackDeployLib)) {
Arguments.push_back("-force_load");
if (forceLoad)
Arguments.push_back("-force_load");
Arguments.push_back(context.Args.MakeArgString(BackDeployLib));
}
};

#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \
addBackDeployLib( \
llvm::VersionTuple Version, BackDeployLibFilter::Filter, LibraryName);
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName, ForceLoad) \
addBackDeployLib( \
llvm::VersionTuple Version, BackDeployLibFilter::Filter, \
LibraryName, ForceLoad);
#include "swift/Frontend/BackDeploymentLibs.def"
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2563,6 +2563,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
runtimeCompatibilityVersion = llvm::VersionTuple(5, 5);
} else if (version.equals("5.6")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 6);
} else if (version.equals("5.8")) {
runtimeCompatibilityVersion = llvm::VersionTuple(5, 8);
} else {
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
versionArg->getAsString(Args), version);
Expand Down
9 changes: 5 additions & 4 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,8 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
// harmless aside from code size.
if (!IRGen.Opts.UseJIT) {
auto addBackDeployLib = [&](llvm::VersionTuple version,
StringRef libraryName) {
StringRef libraryName,
bool forceLoad) {
Optional<llvm::VersionTuple> compatibilityVersion;
if (libraryName == "swiftCompatibilityDynamicReplacements") {
compatibilityVersion = IRGen.Opts.
Expand All @@ -532,11 +533,11 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {

this->addLinkLibrary(LinkLibrary(libraryName,
LibraryKind::Library,
/*forceLoad*/ true));
forceLoad));
};

#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName) \
addBackDeployLib(llvm::VersionTuple Version, LibraryName);
#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName, ForceLoad) \
addBackDeployLib(llvm::VersionTuple Version, LibraryName, ForceLoad);
#include "swift/Frontend/BackDeploymentLibs.def"
}
}
Expand Down
112 changes: 33 additions & 79 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2008,70 +2008,59 @@ static void fixAvailability(SourceRange ReferenceRange,
}
}

void TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
void TypeChecker::diagnosePotentialUnavailability(
SourceRange ReferenceRange, Diag<StringRef, llvm::VersionTuple> Diag,
const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason) {
ASTContext &Context = ReferenceDC->getASTContext();

auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err =
Context.Diags.diagnose(
ReferenceRange.Start, diag::availability_opaque_types_only_version_newer,
ReferenceRange.Start, Diag,
prettyPlatformString(targetPlatform(Context.LangOpts)),
Reason.getRequiredOSVersionRange().getLowerEndpoint());

// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
ReferenceDC,
RequiredRange, Context, Err))
return;
}
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

static void diagnosePotentialConcurrencyUnavailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason) {
ASTContext &Context = ReferenceDC->getASTContext();

auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err =
Context.Diags.diagnose(
ReferenceRange.Start,
diag::availability_concurrency_only_version_newer,
prettyPlatformString(targetPlatform(Context.LangOpts)),
Reason.getRequiredOSVersionRange().getLowerEndpoint());

// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
ReferenceDC,
RequiredRange, Context, Err))
if (fixAvailabilityByNarrowingNearbyVersionCheck(
ReferenceRange, ReferenceDC, RequiredRange, Context, Err))
return;
}
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

void TypeChecker::checkConcurrencyAvailability(SourceRange ReferenceRange,
const DeclContext *ReferenceDC) {
// Check the availability of concurrency runtime support.
bool TypeChecker::checkAvailability(SourceRange ReferenceRange,
AvailabilityContext Availability,
Diag<StringRef, llvm::VersionTuple> Diag,
const DeclContext *ReferenceDC) {
ASTContext &ctx = ReferenceDC->getASTContext();
if (ctx.LangOpts.DisableAvailabilityChecking)
return;
return false;

if (!shouldCheckAvailability(ReferenceDC->getAsDecl()))
return;
return false;

auto runningOS =
TypeChecker::overApproximateAvailabilityAtLocation(
ReferenceRange.Start, ReferenceDC);
auto availability = ctx.getBackDeployedConcurrencyAvailability();
if (!runningOS.isContainedIn(availability)) {
diagnosePotentialConcurrencyUnavailability(
ReferenceRange, ReferenceDC,
UnavailabilityReason::requiresVersionRange(availability.getOSVersion()));
if (!runningOS.isContainedIn(Availability)) {
diagnosePotentialUnavailability(
ReferenceRange, Diag, ReferenceDC,
UnavailabilityReason::requiresVersionRange(Availability.getOSVersion()));
return true;
}

return false;
}

void TypeChecker::checkConcurrencyAvailability(SourceRange ReferenceRange,
const DeclContext *ReferenceDC) {
checkAvailability(
ReferenceRange,
ReferenceDC->getASTContext().getBackDeployedConcurrencyAvailability(),
diag::availability_concurrency_only_version_newer,
ReferenceDC);
}

/// Returns the diagnostic to emit for the potentially unavailable decl and sets
Expand Down Expand Up @@ -3002,48 +2991,13 @@ bool isSubscriptReturningString(const ValueDecl *D, ASTContext &Context) {
return resultTy->isString();
}

static bool diagnosePotentialParameterizedProtocolUnavailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC,
const UnavailabilityReason &Reason) {
ASTContext &Context = ReferenceDC->getASTContext();

auto RequiredRange = Reason.getRequiredOSVersionRange();
{
auto Err = Context.Diags.diagnose(
ReferenceRange.Start,
diag::availability_parameterized_protocol_only_version_newer,
prettyPlatformString(targetPlatform(Context.LangOpts)),
Reason.getRequiredOSVersionRange().getLowerEndpoint());

// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(
ReferenceRange, ReferenceDC, RequiredRange, Context, Err))
return true;
}
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
return true;
}

bool swift::diagnoseParameterizedProtocolAvailability(
SourceRange ReferenceRange, const DeclContext *ReferenceDC) {
// Check the availability of parameterized existential runtime support.
ASTContext &ctx = ReferenceDC->getASTContext();
if (ctx.LangOpts.DisableAvailabilityChecking)
return false;

if (!shouldCheckAvailability(ReferenceDC->getAsDecl()))
return false;

auto runningOS = TypeChecker::overApproximateAvailabilityAtLocation(
ReferenceRange.Start, ReferenceDC);
auto availability = ctx.getParameterizedExistentialRuntimeAvailability();
if (!runningOS.isContainedIn(availability)) {
return diagnosePotentialParameterizedProtocolUnavailability(
ReferenceRange, ReferenceDC,
UnavailabilityReason::requiresVersionRange(
availability.getOSVersion()));
}
return false;
return TypeChecker::checkAvailability(
ReferenceRange,
ReferenceDC->getASTContext().getParameterizedExistentialRuntimeAvailability(),
diag::availability_parameterized_protocol_only_version_newer,
ReferenceDC);
}

static void
Expand Down
6 changes: 6 additions & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,12 @@ static void checkGenericParams(GenericContext *ownerCtx) {
decl->diagnose(diag::experimental_type_with_parameter_pack);
}

TypeChecker::checkAvailability(
gp->getSourceRange(),
ownerCtx->getASTContext().getVariadicGenericTypeAvailability(),
diag::availability_variadic_type_only_version_newer,
ownerCtx);

if (hasPack) {
gp->diagnose(diag::more_than_one_pack_in_type);
}
Expand Down
18 changes: 5 additions & 13 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,11 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
}

// Check the availability of the opaque type runtime support.
if (!ctx.LangOpts.DisableAvailabilityChecking) {
auto runningOS =
TypeChecker::overApproximateAvailabilityAtLocation(
repr->getLoc(),
originatingDecl->getInnermostDeclContext());
auto availability = ctx.getOpaqueTypeAvailability();
if (!runningOS.isContainedIn(availability)) {
TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
repr->getSourceRange(),
originatingDecl->getInnermostDeclContext(),
UnavailabilityReason::requiresVersionRange(availability.getOSVersion()));
}
}
TypeChecker::checkAvailability(
repr->getSourceRange(),
ctx.getOpaqueTypeAvailability(),
diag::availability_opaque_types_only_version_newer,
originatingDecl->getInnermostDeclContext());

// Create a generic signature for the opaque environment. This is the outer
// generic signature with an added generic parameters representing the opaque
Expand Down
Loading