diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index fb329b36eb1e0..c997a9b6b93ba 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1302,7 +1302,8 @@ class SynthesizedProtocolAttr : public DeclAttribute { /// type list. class SpecializeAttr final : public DeclAttribute, - private llvm::TrailingObjects { + private llvm::TrailingObjects { friend class SpecializeAttrTargetDeclRequest; friend TrailingObjects; @@ -1321,35 +1322,44 @@ class SpecializeAttr final LazyMemberLoader *resolver = nullptr; uint64_t resolverContextData; size_t numSPIGroups; + size_t numAvailableAttrs; SpecializeAttr(SourceLoc atLoc, SourceRange Range, TrailingWhereClause *clause, bool exported, SpecializationKind kind, GenericSignature specializedSignature, - DeclNameRef targetFunctionName, - ArrayRef spiGroups); + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs); public: - static SpecializeAttr *create(ASTContext &Ctx, SourceLoc atLoc, - SourceRange Range, TrailingWhereClause *clause, - bool exported, SpecializationKind kind, - DeclNameRef targetFunctionName, - ArrayRef spiGroups, - GenericSignature specializedSignature - = nullptr); + static SpecializeAttr * + create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range, + TrailingWhereClause *clause, bool exported, SpecializationKind kind, + DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availabilityAttrs, + GenericSignature specializedSignature = nullptr); static SpecializeAttr *create(ASTContext &ctx, bool exported, SpecializationKind kind, ArrayRef spiGroups, + ArrayRef availabilityAttrs, GenericSignature specializedSignature, DeclNameRef replacedFunction); static SpecializeAttr *create(ASTContext &ctx, bool exported, SpecializationKind kind, ArrayRef spiGroups, + ArrayRef availabilityAttrs, GenericSignature specializedSignature, DeclNameRef replacedFunction, LazyMemberLoader *resolver, uint64_t data); + size_t numTrailingObjects(OverloadToken) const { + return numSPIGroups; + } + + size_t numTrailingObjects(OverloadToken) const { + return numAvailableAttrs; + } /// Name of SPIs declared by the attribute. /// /// Note: A single SPI name per attribute is currently supported but this @@ -1359,6 +1369,11 @@ class SpecializeAttr final numSPIGroups }; } + ArrayRef getAvailabeAttrs() const { + return {this->template getTrailingObjects(), + numAvailableAttrs}; + } + TrailingWhereClause *getTrailingWhereClause() const; GenericSignature getSpecializedSignature() const { diff --git a/include/swift/AST/Availability.h b/include/swift/AST/Availability.h index 98d2d64b94419..3b670adba29e6 100644 --- a/include/swift/AST/Availability.h +++ b/include/swift/AST/Availability.h @@ -316,6 +316,9 @@ class AvailabilityInference { static Optional annotatedAvailableRange(const Decl *D, ASTContext &C); + static AvailabilityContext + annotatedAvailableRangeForAttr(const SpecializeAttr* attr, ASTContext &ctx); + }; } // end namespace swift diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index f06addefe94b6..185ed162c55ff 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1399,6 +1399,9 @@ ERROR(attr_expected_lparen,none, ERROR(attr_expected_rparen,none, "expected ')' in '%0' %select{attribute|modifier}1", (StringRef, bool)) +ERROR(attr_expected_semi,none, +"expected ';' in '%0' %select{attribute|modifier}1", (StringRef, bool)) + ERROR(attr_expected_comma,none, "expected ',' in '%0' %select{attribute|modifier}1", (StringRef, bool)) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 4adb48315786b..9873778d2e4f2 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1049,7 +1049,7 @@ class Parser { /// \p Attr is where to store the parsed attribute bool parseSpecializeAttribute( swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, - SpecializeAttr *&Attr, + SpecializeAttr *&Attr, AvailabilityContext *SILAvailability, llvm::function_ref parseSILTargetName = [](Parser &) { return false; }, llvm::function_ref parseSILSIPModule = @@ -1060,7 +1060,9 @@ class Parser { swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, + AvailabilityContext *SILAvailability, SmallVectorImpl &spiGroups, + SmallVectorImpl &availableAttrs, llvm::function_ref parseSILTargetName, llvm::function_ref parseSILSIPModule); @@ -1850,7 +1852,11 @@ class Parser { parsePlatformVersionConstraintSpec(); ParserResult parsePlatformAgnosticVersionConstraintSpec(); - + bool + parseAvailability(bool parseAsPartOfSpecializeAttr, StringRef AttrName, + bool &DiscardAttribute, SourceRange &attrRange, + SourceLoc AtLoc, SourceLoc Loc, + llvm::function_ref addAttribute); //===--------------------------------------------------------------------===// // Code completion second pass. diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index b1e3379e5ec0e..870ededef4595 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -72,7 +72,8 @@ class SILSpecializeAttr final { GenericSignature specializedSignature, bool exported, SpecializationKind kind, SILFunction *target, Identifier spiGroup, - const ModuleDecl *spiModule); + const ModuleDecl *spiModule, + AvailabilityContext availability); bool isExported() const { return exported; @@ -110,6 +111,10 @@ class SILSpecializeAttr final { return spiModule; } + AvailabilityContext getAvailability() const { + return availability; + } + void print(llvm::raw_ostream &OS) const; private: @@ -117,13 +122,15 @@ class SILSpecializeAttr final { bool exported; GenericSignature specializedSignature; Identifier spiGroup; + AvailabilityContext availability; const ModuleDecl *spiModule = nullptr; SILFunction *F = nullptr; SILFunction *targetFunction = nullptr; SILSpecializeAttr(bool exported, SpecializationKind kind, GenericSignature specializedSignature, SILFunction *target, - Identifier spiGroup, const ModuleDecl *spiModule); + Identifier spiGroup, const ModuleDecl *spiModule, + AvailabilityContext availability); }; /// SILFunction - A function body that has been lowered to SIL. This consists of diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index f64f7562edcd4..bdd4bdf73303c 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -430,10 +430,11 @@ static bool isShortFormAvailabilityImpliedByOther(const AvailableAttr *Attr, /// @available(OSX 10.10, iOS 8.0, *) static void printShortFormAvailable(ArrayRef Attrs, ASTPrinter &Printer, - const PrintOptions &Options) { + const PrintOptions &Options, + bool forAtSpecialize = false) { assert(!Attrs.empty()); - - Printer << "@available("; + if (!forAtSpecialize) + Printer << "@available("; auto FirstAvail = cast(Attrs.front()); if (Attrs.size() == 1 && FirstAvail->getPlatformAgnosticAvailability() != @@ -445,8 +446,9 @@ static void printShortFormAvailable(ArrayRef Attrs, assert(FirstAvail->isPackageDescriptionVersionSpecific()); Printer << "_PackageDescription "; } - Printer << FirstAvail->Introduced.getValue().getAsString() - << ")"; + Printer << FirstAvail->Introduced.getValue().getAsString(); + if (!forAtSpecialize) + Printer << ")"; } else { for (auto *DA : Attrs) { auto *AvailAttr = cast(DA); @@ -458,9 +460,12 @@ static void printShortFormAvailable(ArrayRef Attrs, Printer << platformString(AvailAttr->Platform) << " " << AvailAttr->Introduced.getValue().getAsString() << ", "; } - Printer << "*)"; + Printer << "*"; + if (!forAtSpecialize) + Printer << ")"; } - Printer.printNewline(); + if (!forAtSpecialize) + Printer.printNewline(); } /// The kind of a parameter in a `wrt:` differentiation parameters clause: @@ -745,6 +750,53 @@ SourceLoc DeclAttributes::getStartLoc(bool forModifiers) const { return lastAttr ? lastAttr->getRangeWithAt().Start : SourceLoc(); } +static void printAvailableAttr(const AvailableAttr *Attr, ASTPrinter &Printer, + const PrintOptions &Options) { + if (Attr->isLanguageVersionSpecific()) + Printer << "swift"; + else if (Attr->isPackageDescriptionVersionSpecific()) + Printer << "_PackageDescription"; + else + Printer << Attr->platformString(); + + if (Attr->isUnconditionallyUnavailable()) + Printer << ", unavailable"; + else if (Attr->isUnconditionallyDeprecated()) + Printer << ", deprecated"; + + if (Attr->Introduced) + Printer << ", introduced: " << Attr->Introduced.getValue().getAsString(); + if (Attr->Deprecated) + Printer << ", deprecated: " << Attr->Deprecated.getValue().getAsString(); + if (Attr->Obsoleted) + Printer << ", obsoleted: " << Attr->Obsoleted.getValue().getAsString(); + + if (!Attr->Rename.empty()) { + Printer << ", renamed: \"" << Attr->Rename << "\""; + } else if (Attr->RenameDecl) { + Printer << ", renamed: \""; + if (auto *Accessor = dyn_cast(Attr->RenameDecl)) { + SmallString<32> Name; + llvm::raw_svector_ostream OS(Name); + Accessor->printUserFacingName(OS); + Printer << Name.str(); + } else { + Printer << Attr->RenameDecl->getName(); + } + Printer << "\""; + } + + // If there's no message, but this is specifically an imported + // "unavailable in Swift" attribute, synthesize a message to look good in + // the generated interface. + if (!Attr->Message.empty()) { + Printer << ", message: "; + Printer.printEscapedStringLiteral(Attr->Message); + } else if (Attr->getPlatformAgnosticAvailability() == + PlatformAgnosticAvailabilityKind::UnavailableInSwift) + Printer << ", message: \"Not available in Swift\""; +} + bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, const Decl *D) const { @@ -890,51 +942,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer.printAttrName("@available"); Printer << "("; auto Attr = cast(this); - if (Attr->isLanguageVersionSpecific()) - Printer << "swift"; - else if (Attr->isPackageDescriptionVersionSpecific()) - Printer << "_PackageDescription"; - else - Printer << Attr->platformString(); - - if (Attr->isUnconditionallyUnavailable()) - Printer << ", unavailable"; - else if (Attr->isUnconditionallyDeprecated()) - Printer << ", deprecated"; - - if (Attr->Introduced) - Printer << ", introduced: " << Attr->Introduced.getValue().getAsString(); - if (Attr->Deprecated) - Printer << ", deprecated: " << Attr->Deprecated.getValue().getAsString(); - if (Attr->Obsoleted) - Printer << ", obsoleted: " << Attr->Obsoleted.getValue().getAsString(); - - if (!Attr->Rename.empty()) { - Printer << ", renamed: \"" << Attr->Rename << "\""; - } else if (Attr->RenameDecl) { - Printer << ", renamed: \""; - if (auto *Accessor = dyn_cast(Attr->RenameDecl)) { - SmallString<32> Name; - llvm::raw_svector_ostream OS(Name); - Accessor->printUserFacingName(OS); - Printer << Name.str(); - } else { - Printer << Attr->RenameDecl->getName(); - } - Printer << "\""; - } - - // If there's no message, but this is specifically an imported - // "unavailable in Swift" attribute, synthesize a message to look good in - // the generated interface. - if (!Attr->Message.empty()) { - Printer << ", message: "; - Printer.printEscapedStringLiteral(Attr->Message); - } - else if (Attr->getPlatformAgnosticAvailability() - == PlatformAgnosticAvailabilityKind::UnavailableInSwift) - Printer << ", message: \"Not available in Swift\""; - + printAvailableAttr(Attr, Printer, Options); Printer << ")"; break; } @@ -984,6 +992,21 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << "kind: " << kind << ", "; if (target) Printer << "target: " << target << ", "; + auto availAttrs = attr->getAvailabeAttrs(); + if (!availAttrs.empty()) { + Printer << "availability: "; + auto numAttrs = availAttrs.size(); + if (numAttrs == 1) { + printAvailableAttr(availAttrs[0], Printer, Options); + Printer << "; "; + } else { + SmallVector tmp(availAttrs.begin(), + availAttrs.end()); + printShortFormAvailable(tmp, Printer, Options, + true /*forAtSpecialize*/); + Printer << "; "; + } + } SmallVector requirementsScratch; auto requirements = attr->getSpecializedSignature().getRequirements(); auto *FnDecl = dyn_cast_or_null(D); @@ -1663,13 +1686,18 @@ SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range, SpecializationKind kind, GenericSignature specializedSignature, DeclNameRef targetFunctionName, - ArrayRef spiGroups) + ArrayRef spiGroups, + ArrayRef availableAttrs) : DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/clause == nullptr), trailingWhereClause(clause), specializedSignature(specializedSignature), - targetFunctionName(targetFunctionName), numSPIGroups(spiGroups.size()) { + targetFunctionName(targetFunctionName), numSPIGroups(spiGroups.size()), + numAvailableAttrs(availableAttrs.size()) { std::uninitialized_copy(spiGroups.begin(), spiGroups.end(), getTrailingObjects()); + std::uninitialized_copy(availableAttrs.begin(), availableAttrs.end(), + getTrailingObjects()); + Bits.SpecializeAttr.exported = exported; Bits.SpecializeAttr.kind = unsigned(kind); } @@ -1684,35 +1712,41 @@ SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc, bool exported, SpecializationKind kind, DeclNameRef targetFunctionName, ArrayRef spiGroups, + ArrayRef availableAttrs, GenericSignature specializedSignature) { - unsigned size = totalSizeToAlloc(spiGroups.size()); + unsigned size = totalSizeToAlloc( + spiGroups.size(), availableAttrs.size()); void *mem = Ctx.Allocate(size, alignof(SpecializeAttr)); return new (mem) SpecializeAttr(atLoc, range, clause, exported, kind, specializedSignature, - targetFunctionName, spiGroups); + targetFunctionName, spiGroups, availableAttrs); } SpecializeAttr *SpecializeAttr::create(ASTContext &ctx, bool exported, SpecializationKind kind, ArrayRef spiGroups, + ArrayRef availableAttrs, GenericSignature specializedSignature, DeclNameRef targetFunctionName) { - unsigned size = totalSizeToAlloc(spiGroups.size()); + unsigned size = totalSizeToAlloc( + spiGroups.size(), availableAttrs.size()); void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); - return new (mem) - SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, - specializedSignature, targetFunctionName, spiGroups); + return new (mem) SpecializeAttr( + SourceLoc(), SourceRange(), nullptr, exported, kind, specializedSignature, + targetFunctionName, spiGroups, availableAttrs); } SpecializeAttr *SpecializeAttr::create( ASTContext &ctx, bool exported, SpecializationKind kind, - ArrayRef spiGroups, GenericSignature specializedSignature, - DeclNameRef targetFunctionName, LazyMemberLoader *resolver, uint64_t data) { - unsigned size = totalSizeToAlloc(spiGroups.size()); + ArrayRef spiGroups, ArrayRef availableAttrs, + GenericSignature specializedSignature, DeclNameRef targetFunctionName, + LazyMemberLoader *resolver, uint64_t data) { + unsigned size = totalSizeToAlloc( + spiGroups.size(), availableAttrs.size()); void *mem = ctx.Allocate(size, alignof(SpecializeAttr)); - auto *attr = new (mem) - SpecializeAttr(SourceLoc(), SourceRange(), nullptr, exported, kind, - specializedSignature, targetFunctionName, spiGroups); + auto *attr = new (mem) SpecializeAttr( + SourceLoc(), SourceRange(), nullptr, exported, kind, specializedSignature, + targetFunctionName, spiGroups, availableAttrs); attr->resolver = resolver; attr->resolverContextData = data; return attr; diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 4fa020ff0de9e..9f284ff5833be 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -176,6 +176,33 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) { VersionRange::allGTE(bestAvailAttr->Introduced.getValue())}; } +AvailabilityContext +AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr, + ASTContext &ctx) { + + const AvailableAttr *bestAvailAttr = nullptr; + + for (auto *availAttr : attr->getAvailabeAttrs()) { + if (availAttr == nullptr || !availAttr->Introduced.hasValue() || + !availAttr->isActivePlatform(ctx) || + availAttr->isLanguageVersionSpecific() || + availAttr->isPackageDescriptionVersionSpecific()) { + continue; + } + + if (isBetterThan(availAttr, bestAvailAttr)) + bestAvailAttr = availAttr; + } + + if (bestAvailAttr) { + return AvailabilityContext{ + VersionRange::allGTE(bestAvailAttr->Introduced.getValue()) + }; + } + + return AvailabilityContext::alwaysAvailable(); +} + AvailabilityContext AvailabilityInference::availableRange(const Decl *D, ASTContext &Ctx) { Optional AnnotatedRange = diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7649b49c03725..e0851d711d24a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -582,26 +582,32 @@ bool Parser::parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool &DiscardAttribute, Optional &Exported, Optional &Kind, swift::TrailingWhereClause *&TrailingWhereClause, - DeclNameRef &targetFunction, SmallVectorImpl &spiGroups, + DeclNameRef &targetFunction, AvailabilityContext *SILAvailability, SmallVectorImpl &spiGroups, + SmallVectorImpl &availableAttrs, llvm::function_ref parseSILTargetName, llvm::function_ref parseSILSIPModule) { + bool isSIL = SILAvailability != nullptr; SyntaxParsingContext ContentContext(SyntaxContext, SyntaxKind::SpecializeAttributeSpecList); // Parse optional "exported" and "kind" labeled parameters. while (!Tok.is(tok::kw_where)) { + bool isAvailability = false; if (Tok.is(tok::identifier)) { auto ParamLabel = Tok.getText(); SyntaxParsingContext ArgumentContext( SyntaxContext, ParamLabel == "target" - ? SyntaxKind::TargetFunctionEntry - : SyntaxKind::LabeledSpecializeEntry); + ? SyntaxKind::TargetFunctionEntry : + (ParamLabel == "availability" ? + SyntaxKind::AvailabilityEntry + : SyntaxKind::LabeledSpecializeEntry)); if (ParamLabel != "exported" && ParamLabel != "kind" && ParamLabel != "target" && ParamLabel != "spi" && - ParamLabel != "spiModule") { + ParamLabel != "spiModule" && ParamLabel != "availability" && + (!isSIL || ParamLabel != "available")) { diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name, ParamLabel); } - consumeToken(); + auto AtLoc = consumeToken(); if (!consumeIf(tok::colon)) { diagnose(Tok.getLoc(), diag::attr_specialize_missing_colon, ParamLabel); skipUntil(tok::comma, tok::kw_where); @@ -623,6 +629,26 @@ bool Parser::parseSpecializeAttributeArguments( diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined, ParamLabel); } + if (ParamLabel == "available") { + SourceRange range; + llvm::VersionTuple version; + if (parseVersionTuple(version, range, + diag::sil_availability_expected_version)) + return false; + + *SILAvailability = AvailabilityContext(VersionRange::allGTE(version)); + } + if (ParamLabel == "availability") { + SourceRange attrRange; + auto Loc = Tok.getLoc(); + bool shouldDiscardAvailabilityAttr = false; + isAvailability = true; + if (!parseAvailability(true, ParamLabel, shouldDiscardAvailabilityAttr, + attrRange, AtLoc, Loc, [&](AvailableAttr *attr) { + availableAttrs.push_back(attr); + })) + return false; + } if (ParamLabel == "exported") { bool isTrue = consumeIf(tok::kw_true); bool isFalse = consumeIf(tok::kw_false); @@ -692,7 +718,7 @@ bool Parser::parseSpecializeAttributeArguments( spiGroups.push_back(Context.getIdentifier(text)); consumeToken(); } - if (!consumeIf(tok::comma)) { + if (!isAvailability && !consumeIf(tok::comma)) { diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma); skipUntil(tok::comma, tok::kw_where); if (Tok.is(ClosingBrace)) @@ -727,9 +753,142 @@ bool Parser::parseSpecializeAttributeArguments( return true; } +bool Parser::parseAvailability( + bool parseAsPartOfSpecializeAttr, StringRef AttrName, + bool &DiscardAttribute, SourceRange &attrRange, SourceLoc AtLoc, + SourceLoc Loc, llvm::function_ref addAttribute) { + // platform: + // * + // identifier + if (!Tok.is(tok::identifier) && + !(Tok.isAnyOperator() && Tok.getText() == "*")) { + if (Tok.is(tok::code_complete) && CodeCompletion) { + CodeCompletion->completeDeclAttrParam(DAK_Available, 0); + consumeToken(tok::code_complete); + } + diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) + .highlight(SourceRange(Tok.getLoc())); + consumeIf(tok::r_paren); + return false; + } + // Delay processing of platform until later, after we have + // parsed more of the attribute. + StringRef Platform = Tok.getText(); + + if (Platform != "*" && + (peekToken().isAny(tok::integer_literal, tok::floating_literal) || + peekAvailabilityMacroName())) { + // We have the short form of available: @available(iOS 8.0.1, *) + SmallVector Specs; + ParserStatus Status = + parseAvailabilitySpecList(Specs, AvailabilitySpecSource::Available); + + if (Status.isErrorOrHasCompletion()) + return false; + + auto availTerminator = + parseAsPartOfSpecializeAttr ? tok::semi : tok::r_paren; + if (!consumeIf(availTerminator)) { + auto diagnostic = parseAsPartOfSpecializeAttr + ? diag::attr_expected_semi + : diag::attr_expected_rparen; + diagnose(Tok.getLoc(), diagnostic, AttrName, parseAsPartOfSpecializeAttr); + return false; + } + + attrRange = SourceRange(Loc, PreviousLoc); + // For each platform version spec in the spec list, create an + // implicit AvailableAttr for the platform with the introduced + // version from the spec. For example, if we have + // @available(iOS 8.0, OSX 10.10, *): + // we will synthesize: + // @available(iOS, introduced: 8.0) + // @available(OSX, introduced: 10.10) + // + // Similarly if we have a language version spec or PackageDescription + // version in the spec list, create an implicit AvailableAttr + // with the specified version as the introduced argument. + // For example, if we have + // @available(swift 3.1) + // we will synthesize + // @available(swift, introduced: 3.1) + // or, if we have + // @available(_PackageDescription 4.2) + // we will synthesize + // @available(_PackageDescription, introduced: 4.2) + + for (auto *Spec : Specs) { + PlatformKind Platform; + llvm::VersionTuple Version; + SourceRange VersionRange; + PlatformAgnosticAvailabilityKind PlatformAgnostic; + + if (auto *PlatformVersionSpec = + dyn_cast(Spec)) { + Platform = PlatformVersionSpec->getPlatform(); + Version = PlatformVersionSpec->getVersion(); + VersionRange = PlatformVersionSpec->getVersionSrcRange(); + PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; + + } else if (auto *PlatformAgnosticVersionSpec = dyn_cast< + PlatformAgnosticVersionConstraintAvailabilitySpec>(Spec)) { + Platform = PlatformKind::none; + Version = PlatformAgnosticVersionSpec->getVersion(); + VersionRange = PlatformAgnosticVersionSpec->getVersionSrcRange(); + PlatformAgnostic = + PlatformAgnosticVersionSpec->isLanguageVersionSpecific() + ? PlatformAgnosticAvailabilityKind::SwiftVersionSpecific + : PlatformAgnosticAvailabilityKind:: + PackageDescriptionVersionSpecific; + + } else { + continue; + } + + Version = canonicalizePlatformVersion(Platform, Version); + + addAttribute(new (Context) AvailableAttr( + AtLoc, attrRange, Platform, + /*Message=*/StringRef(), + /*Rename=*/StringRef(), + /*RenameDecl=*/nullptr, + /*Introduced=*/Version, + /*IntroducedRange=*/VersionRange, + /*Deprecated=*/llvm::VersionTuple(), + /*DeprecatedRange=*/SourceRange(), + /*Obsoleted=*/llvm::VersionTuple(), + /*ObsoletedRange=*/SourceRange(), PlatformAgnostic, + /*Implicit=*/false)); + } + + return true; + } + + auto AvailabilityAttr = + parseExtendedAvailabilitySpecList(AtLoc, Loc, AttrName); + DiscardAttribute |= AvailabilityAttr.isParseErrorOrHasCompletion(); + + auto availTerminator = parseAsPartOfSpecializeAttr ? tok::semi : tok::r_paren; + if (!consumeIf(availTerminator)) { + if (!DiscardAttribute) { + auto diagnostic = parseAsPartOfSpecializeAttr ? diag::attr_expected_semi + : diag::attr_expected_rparen; + diagnose(Tok.getLoc(), diagnostic, AttrName, parseAsPartOfSpecializeAttr); + } + return false; + } + + if (!DiscardAttribute) { + addAttribute(AvailabilityAttr.get()); + } else { + return false; + } + return true; +} + bool Parser::parseSpecializeAttribute( swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, - SpecializeAttr *&Attr, + SpecializeAttr *&Attr, AvailabilityContext *SILAvailability, llvm::function_ref parseSILTargetName, llvm::function_ref parseSILSIPModule) { assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square); @@ -745,9 +904,11 @@ bool Parser::parseSpecializeAttribute( DeclNameRef targetFunction; SmallVector spiGroups; + SmallVector availableAttrs; if (!parseSpecializeAttributeArguments( ClosingBrace, DiscardAttribute, exported, kind, trailingWhereClause, - targetFunction, spiGroups, parseSILTargetName, parseSILSIPModule)) { + targetFunction, SILAvailability, spiGroups, availableAttrs, parseSILTargetName, + parseSILSIPModule)) { return false; } @@ -776,7 +937,8 @@ bool Parser::parseSpecializeAttribute( // Store the attribute. Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc), trailingWhereClause, exported.getValue(), - kind.getValue(), targetFunction, spiGroups); + kind.getValue(), targetFunction, spiGroups, + availableAttrs); return true; } @@ -2211,128 +2373,10 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttribute::isDeclModifier(DK)); return false; } - - // platform: - // * - // identifier - if (!Tok.is(tok::identifier) && - !(Tok.isAnyOperator() && Tok.getText() == "*")) { - if (Tok.is(tok::code_complete) && CodeCompletion) { - CodeCompletion->completeDeclAttrParam(DAK_Available, 0); - consumeToken(tok::code_complete); - } - diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName) - .highlight(SourceRange(Tok.getLoc())); - consumeIf(tok::r_paren); - return false; - } - - // Delay processing of platform until later, after we have - // parsed more of the attribute. - StringRef Platform = Tok.getText(); - - if (Platform != "*" && - (peekToken().isAny(tok::integer_literal, tok::floating_literal) || - peekAvailabilityMacroName())) { - // We have the short form of available: @available(iOS 8.0.1, *) - SmallVector Specs; - ParserStatus Status = parseAvailabilitySpecList(Specs, - AvailabilitySpecSource::Available); - - if (Status.isErrorOrHasCompletion()) - return false; - - if (!consumeIf(tok::r_paren)) { - diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - return false; - } - - AttrRange = SourceRange(Loc, PreviousLoc); - // For each platform version spec in the spec list, create an - // implicit AvailableAttr for the platform with the introduced - // version from the spec. For example, if we have - // @available(iOS 8.0, OSX 10.10, *): - // we will synthesize: - // @available(iOS, introduced: 8.0) - // @available(OSX, introduced: 10.10) - // - // Similarly if we have a language version spec or PackageDescription - // version in the spec list, create an implicit AvailableAttr - // with the specified version as the introduced argument. - // For example, if we have - // @available(swift 3.1) - // we will synthesize - // @available(swift, introduced: 3.1) - // or, if we have - // @available(_PackageDescription 4.2) - // we will synthesize - // @available(_PackageDescription, introduced: 4.2) - - for (auto *Spec : Specs) { - PlatformKind Platform; - llvm::VersionTuple Version; - SourceRange VersionRange; - PlatformAgnosticAvailabilityKind PlatformAgnostic; - - if (auto *PlatformVersionSpec = - dyn_cast(Spec)) { - Platform = PlatformVersionSpec->getPlatform(); - Version = PlatformVersionSpec->getVersion(); - VersionRange = PlatformVersionSpec->getVersionSrcRange(); - PlatformAgnostic = PlatformAgnosticAvailabilityKind::None; - - } else if (auto *PlatformAgnosticVersionSpec = - dyn_cast(Spec)) { - Platform = PlatformKind::none; - Version = PlatformAgnosticVersionSpec->getVersion(); - VersionRange = PlatformAgnosticVersionSpec->getVersionSrcRange(); - PlatformAgnostic = PlatformAgnosticVersionSpec->isLanguageVersionSpecific() ? - PlatformAgnosticAvailabilityKind::SwiftVersionSpecific : - PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific; - - } else { - continue; - } - - Version = canonicalizePlatformVersion(Platform, Version); - - Attributes.add(new (Context) - AvailableAttr(AtLoc, AttrRange, - Platform, - /*Message=*/StringRef(), - /*Rename=*/StringRef(), - /*RenameDecl=*/nullptr, - /*Introduced=*/Version, - /*IntroducedRange=*/VersionRange, - /*Deprecated=*/llvm::VersionTuple(), - /*DeprecatedRange=*/SourceRange(), - /*Obsoleted=*/llvm::VersionTuple(), - /*ObsoletedRange=*/SourceRange(), - PlatformAgnostic, - /*Implicit=*/false)); - } - - break; - } - - auto AvailabilityAttr = parseExtendedAvailabilitySpecList(AtLoc, Loc, - AttrName); - DiscardAttribute |= AvailabilityAttr.isParseErrorOrHasCompletion(); - - if (!consumeIf(tok::r_paren)) { - if (!DiscardAttribute) { - diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - } - return false; - } - - if (!DiscardAttribute) { - Attributes.add(AvailabilityAttr.get()); - } else { + if (!parseAvailability(false, AttrName, DiscardAttribute, AttrRange, AtLoc, + Loc, + [&](AvailableAttr *attr) { Attributes.add(attr); })) return false; - } break; } case DAK_PrivateImport: { @@ -2555,7 +2599,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, return false; } SpecializeAttr *Attr; - if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr)) + if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr, nullptr)) return false; Attributes.add(Attr); diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index d273cf3feac3d..6399c9a3de57c 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -40,9 +40,10 @@ STATISTIC(MaxBitfieldID, "Max value of SILFunction::currentBitfieldID"); SILSpecializeAttr::SILSpecializeAttr(bool exported, SpecializationKind kind, GenericSignature specializedSig, SILFunction *target, Identifier spiGroup, - const ModuleDecl *spiModule) + const ModuleDecl *spiModule, + AvailabilityContext availability) : kind(kind), exported(exported), specializedSignature(specializedSig), - spiGroup(spiGroup), spiModule(spiModule), targetFunction(target) { + spiGroup(spiGroup), availability(availability), spiModule(spiModule), targetFunction(target) { if (targetFunction) targetFunction->incrementRefCount(); } @@ -51,10 +52,11 @@ SILSpecializeAttr * SILSpecializeAttr::create(SILModule &M, GenericSignature specializedSig, bool exported, SpecializationKind kind, SILFunction *target, Identifier spiGroup, - const ModuleDecl *spiModule) { + const ModuleDecl *spiModule, + AvailabilityContext availability) { void *buf = M.allocate(sizeof(SILSpecializeAttr), alignof(SILSpecializeAttr)); return ::new (buf) SILSpecializeAttr(exported, kind, specializedSig, target, - spiGroup, spiModule); + spiGroup, spiModule, availability); } void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 192f6a62be8be..963102cbd0f06 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -80,17 +80,20 @@ void SILFunctionBuilder::addFunctionAttributes( if (hasSPI) { spiGroupIdent = spiGroups[0]; } + auto availability = + AvailabilityInference::annotatedAvailableRangeForAttr(SA, + M.getSwiftModule()->getASTContext()); if (targetFunctionDecl) { SILDeclRef declRef(targetFunctionDecl, constant.kind, false); targetFunction = getOrCreateDeclaration(targetFunctionDecl, declRef); F->addSpecializeAttr(SILSpecializeAttr::create( M, SA->getSpecializedSignature(), SA->isExported(), kind, targetFunction, spiGroupIdent, - attributedFuncDecl->getModuleContext())); + attributedFuncDecl->getModuleContext(), availability)); } else { F->addSpecializeAttr(SILSpecializeAttr::create( M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, - spiGroupIdent, attributedFuncDecl->getModuleContext())); + spiGroupIdent, attributedFuncDecl->getModuleContext(), availability)); } } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f6b671ffb6861..597de3f1e08dc 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3711,6 +3711,10 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { if (targetFunction) { OS << "target: \"" << targetFunction->getName() << "\", "; } + if (!availability.isAlwaysAvailable()) { + auto version = availability.getOSVersion().getLowerEndpoint(); + OS << "available: " << version.getAsString() << ", "; + } if (!requirements.empty()) { OS << "where "; SILFunction *F = getFunction(); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index 316c2d0aac006..feb3dafe5dd8e 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -114,6 +114,7 @@ namespace { SILFunction *target = nullptr; Identifier spiGroupID; ModuleDecl *spiModule; + AvailabilityContext availability = AvailabilityContext::alwaysAvailable(); }; class SILParser { @@ -1092,9 +1093,9 @@ static bool parseDeclSILOptional(bool *isTransparent, SpecializeAttr *Attr; StringRef targetFunctionName; ModuleDecl *module = nullptr; - + AvailabilityContext availability = AvailabilityContext::alwaysAvailable(); if (!SP.P.parseSpecializeAttribute( - tok::r_square, AtLoc, Loc, Attr, + tok::r_square, AtLoc, Loc, Attr, &availability, [&targetFunctionName](Parser &P) -> bool { if (P.Tok.getKind() != tok::string_literal) { P.diagnose(P.Tok, diag::expected_in_attribute_list); @@ -1136,6 +1137,7 @@ static bool parseDeclSILOptional(bool *isTransparent, : SILSpecializeAttr::SpecializationKind::Partial; SpecAttr.exported = Attr->isExported(); SpecAttr.target = targetFunction; + SpecAttr.availability = availability; SpecAttrs->emplace_back(SpecAttr); if (!Attr->getSPIGroups().empty()) { SpecAttr.spiGroupID = Attr->getSPIGroups()[0]; @@ -6279,7 +6281,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { GenericSignature()); FunctionState.F->addSpecializeAttr(SILSpecializeAttr::create( FunctionState.F->getModule(), genericSig, Attr.exported, - Attr.kind, Attr.target, Attr.spiGroupID, Attr.spiModule)); + Attr.kind, Attr.target, Attr.spiGroupID, Attr.spiModule, Attr.availability)); } } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 99ba13ffbbda4..5746cb30e7b20 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -2209,9 +2209,12 @@ static void transferSpecializeAttributeTargets(SILGenModule &SGM, SILModule &M, if (hasSPIGroup) { spiGroupIdent = spiGroups[0]; } + auto availability = + AvailabilityInference::annotatedAvailableRangeForAttr(SA, + M.getSwiftModule()->getASTContext()); targetSILFunction->addSpecializeAttr(SILSpecializeAttr::create( M, SA->getSpecializedSignature(), SA->isExported(), kind, nullptr, - spiGroupIdent, vd->getModuleContext())); + spiGroupIdent, vd->getModuleContext(), availability)); } } } diff --git a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp index 6da6355e52933..51045e3cb723a 100644 --- a/lib/SILOptimizer/Transforms/EagerSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/EagerSpecializer.cpp @@ -794,6 +794,7 @@ static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder, } else if (SA.isExported()) { NewFunc->setLinkage(NewFunc->isDefinition() ? SILLinkage::Public : SILLinkage::PublicExternal); + NewFunc->setAvailabilityForLinkage(SA.getAvailability()); } return NewFunc; diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 7ca5c857fc94a..ff7f23bf7b17f 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2485,6 +2485,19 @@ usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply, !currentModule->isImportedAsSPI(spiGroup, funcModule)) continue; } + // Check whether the availability of the specialization allows for using + // it. We check the deployment target or the current functions availability + // target depending which one is more recent. + auto specializationAvail = SA->getAvailability(); + auto &ctxt = funcBuilder.getModule().getSwiftModule()->getASTContext(); + auto deploymentAvail = AvailabilityContext::forDeploymentTarget(ctxt); + auto currentFnAvailability = apply.getFunction()->getAvailabilityForLinkage(); + if (!currentFnAvailability.isAlwaysAvailable() && + !deploymentAvail.isContainedIn(currentFnAvailability)) + deploymentAvail = currentFnAvailability; + if (!deploymentAvail.isContainedIn(specializationAvail)) + continue; + ReabstractionInfo reInfo(funcBuilder.getModule().getSwiftModule(), funcBuilder.getModule().isWholeModule(), refF, SA->getSpecializedSignature(), @@ -2499,7 +2512,10 @@ usePrespecialized(SILOptFunctionBuilder &funcBuilder, ApplySite apply, mangler.mangleReabstracted(subs, reInfo.needAlternativeMangling()); prespecializedReInfo = reInfo; - return lookupOrCreatePrespecialization(funcBuilder, refF, name, reInfo); + auto fn = lookupOrCreatePrespecialization(funcBuilder, refF, name, reInfo); + if (!specializationAvail.isAlwaysAvailable()) + fn->setAvailabilityForLinkage(specializationAvail); + return fn; } return nullptr; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 996f6929e7dab..2a37d6a3b9dee 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4225,6 +4225,9 @@ class DeclDeserializer { return dtor; } + + AvailableAttr *readAvailable_DECL_ATTR(SmallVectorImpl &scratch, + StringRef blobData); }; } @@ -4267,6 +4270,61 @@ ModuleFile::getDeclChecked( return declOrOffset; } +AvailableAttr * +DeclDeserializer::readAvailable_DECL_ATTR(SmallVectorImpl &scratch, + StringRef blobData) { + bool isImplicit; + bool isUnavailable; + bool isDeprecated; + bool isPackageDescriptionVersionSpecific; + DEF_VER_TUPLE_PIECES(Introduced); + DEF_VER_TUPLE_PIECES(Deprecated); + DEF_VER_TUPLE_PIECES(Obsoleted); + DeclID renameDeclID; + unsigned platform, messageSize, renameSize; + + // Decode the record, pulling the version tuple information. + serialization::decls_block::AvailableDeclAttrLayout::readRecord( + scratch, isImplicit, isUnavailable, isDeprecated, + isPackageDescriptionVersionSpecific, LIST_VER_TUPLE_PIECES(Introduced), + LIST_VER_TUPLE_PIECES(Deprecated), LIST_VER_TUPLE_PIECES(Obsoleted), + platform, renameDeclID, messageSize, renameSize); + + ValueDecl *renameDecl = nullptr; + if (renameDeclID) { + renameDecl = cast(MF.getDecl(renameDeclID)); + } + + StringRef message = blobData.substr(0, messageSize); + blobData = blobData.substr(messageSize); + StringRef rename = blobData.substr(0, renameSize); + llvm::VersionTuple Introduced, Deprecated, Obsoleted; + DECODE_VER_TUPLE(Introduced) + DECODE_VER_TUPLE(Deprecated) + DECODE_VER_TUPLE(Obsoleted) + + PlatformAgnosticAvailabilityKind platformAgnostic; + if (isUnavailable) + platformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; + else if (isDeprecated) + platformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; + else if (((PlatformKind)platform) == PlatformKind::none && + (!Introduced.empty() || !Deprecated.empty() || !Obsoleted.empty())) + platformAgnostic = + isPackageDescriptionVersionSpecific + ? PlatformAgnosticAvailabilityKind:: + PackageDescriptionVersionSpecific + : PlatformAgnosticAvailabilityKind::SwiftVersionSpecific; + else + platformAgnostic = PlatformAgnosticAvailabilityKind::None; + + auto attr = new (ctx) AvailableAttr( + SourceLoc(), SourceRange(), (PlatformKind)platform, message, rename, + renameDecl, Introduced, SourceRange(), Deprecated, SourceRange(), + Obsoleted, SourceRange(), platformAgnostic, isImplicit); + return attr; +} + llvm::Error DeclDeserializer::deserializeDeclCommon() { using namespace decls_block; @@ -4385,60 +4443,7 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { } case decls_block::Available_DECL_ATTR: { - bool isImplicit; - bool isUnavailable; - bool isDeprecated; - bool isPackageDescriptionVersionSpecific; - DEF_VER_TUPLE_PIECES(Introduced); - DEF_VER_TUPLE_PIECES(Deprecated); - DEF_VER_TUPLE_PIECES(Obsoleted); - DeclID renameDeclID; - unsigned platform, messageSize, renameSize; - - // Decode the record, pulling the version tuple information. - serialization::decls_block::AvailableDeclAttrLayout::readRecord( - scratch, isImplicit, isUnavailable, isDeprecated, - isPackageDescriptionVersionSpecific, - LIST_VER_TUPLE_PIECES(Introduced), - LIST_VER_TUPLE_PIECES(Deprecated), - LIST_VER_TUPLE_PIECES(Obsoleted), - platform, renameDeclID, messageSize, renameSize); - - ValueDecl *renameDecl = nullptr; - if (renameDeclID) { - renameDecl = cast(MF.getDecl(renameDeclID)); - } - - StringRef message = blobData.substr(0, messageSize); - blobData = blobData.substr(messageSize); - StringRef rename = blobData.substr(0, renameSize); - llvm::VersionTuple Introduced, Deprecated, Obsoleted; - DECODE_VER_TUPLE(Introduced) - DECODE_VER_TUPLE(Deprecated) - DECODE_VER_TUPLE(Obsoleted) - - PlatformAgnosticAvailabilityKind platformAgnostic; - if (isUnavailable) - platformAgnostic = PlatformAgnosticAvailabilityKind::Unavailable; - else if (isDeprecated) - platformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated; - else if (((PlatformKind)platform) == PlatformKind::none && - (!Introduced.empty() || - !Deprecated.empty() || - !Obsoleted.empty())) - platformAgnostic = isPackageDescriptionVersionSpecific ? - PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific: - PlatformAgnosticAvailabilityKind::SwiftVersionSpecific; - else - platformAgnostic = PlatformAgnosticAvailabilityKind::None; - - Attr = new (ctx) AvailableAttr( - SourceLoc(), SourceRange(), - (PlatformKind)platform, message, rename, renameDecl, - Introduced, SourceRange(), - Deprecated, SourceRange(), - Obsoleted, SourceRange(), - platformAgnostic, isImplicit); + Attr = readAvailable_DECL_ATTR(scratch, blobData); break; } @@ -4475,11 +4480,13 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { ArrayRef rawPieceIDs; uint64_t numArgs; uint64_t numSPIGroups; + uint64_t numAvailabilityAttrs; DeclID targetFunID; serialization::decls_block::SpecializeDeclAttrLayout::readRecord( scratch, exported, specializationKindVal, specializedSigID, - targetFunID, numArgs, numSPIGroups, rawPieceIDs); + targetFunID, numArgs, numSPIGroups, numAvailabilityAttrs, + rawPieceIDs); assert(rawPieceIDs.size() == numArgs + numSPIGroups || rawPieceIDs.size() == (numArgs - 1 + numSPIGroups)); @@ -4509,10 +4516,35 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { for (auto id : rawPieceIDs.slice(numTargetFunctionPiecesToSkip)) spis.push_back(MF.getIdentifier(id)); } + SmallVector availabilityAttrs; + while (numAvailabilityAttrs) { + // Prepare to read the next record. + restoreOffset.cancel(); + scratch.clear(); + + // TODO: deserialize them. + BCOffsetRAII restoreOffset2(MF.DeclTypeCursor); + llvm::BitstreamEntry entry = + MF.fatalIfUnexpected(MF.DeclTypeCursor.advance()); + if (entry.Kind != llvm::BitstreamEntry::Record) { + // We don't know how to serialize decls represented by sub-blocks. + MF.fatal(); + } + unsigned recordID = MF.fatalIfUnexpected( + MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); + if (recordID != decls_block::Available_DECL_ATTR) { + MF.fatal(); + } + + auto attr = readAvailable_DECL_ATTR(scratch, blobData); + availabilityAttrs.push_back(attr); + restoreOffset2.cancel(); + --numAvailabilityAttrs; + } auto specializedSig = MF.getGenericSignature(specializedSigID); Attr = SpecializeAttr::create(ctx, exported != 0, specializationKind, - spis, specializedSig, + spis, availabilityAttrs, specializedSig, replacedFunctionName, &MF, targetFunID); break; } diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 1cc9624e7b570..4c720611d7387 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -725,9 +725,11 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, IdentifierID targetFunctionID; IdentifierID spiGroupID; ModuleID spiModuleID; + unsigned LIST_VER_TUPLE_PIECES(available); SILSpecializeAttrLayout::readRecord( scratch, exported, specializationKindVal, specializedSigID, - targetFunctionID, spiGroupID, spiModuleID); + targetFunctionID, spiGroupID, spiModuleID, + LIST_VER_TUPLE_PIECES(available)); SILFunction *target = nullptr; if (targetFunctionID) { @@ -745,13 +747,19 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, specializationKindVal ? SILSpecializeAttr::SpecializationKind::Partial : SILSpecializeAttr::SpecializationKind::Full; + llvm::VersionTuple available; + DECODE_VER_TUPLE(available); + auto availability = available.empty() + ? AvailabilityContext::alwaysAvailable() + : AvailabilityContext(VersionRange::allGTE(available)); + auto specializedSig = MF->getGenericSignature(specializedSigID); // Only add the specialize attributes once. if (shouldAddAtttributes) { // Read the substitution list and construct a SILSpecializeAttr. fn->addSpecializeAttr(SILSpecializeAttr::create( SILMod, specializedSig, exported != 0, specializationKind, target, - spiGroup, spiModule)); + spiGroup, spiModule, availability)); } } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 932b2a7410b6a..c2a342f08c48a 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 631; // IS_OSSA +const uint16_t SWIFTMODULE_VERSION_MINOR = 632; // _specialize availability /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1933,15 +1933,16 @@ namespace decls_block { >; using SpecializeDeclAttrLayout = BCRecordLayout< - Specialize_DECL_ATTR, - BCFixed<1>, // exported flag - BCFixed<1>, // specialization kind - GenericSignatureIDField, // specialized signature - DeclIDField, // target function - BCVBR<4>, // # of arguments (+1) or 1 if simple decl name, 0 if no target - BCVBR<4>, // # of SPI groups - BCArray // target function pieces, spi groups - >; + Specialize_DECL_ATTR, + BCFixed<1>, // exported flag + BCFixed<1>, // specialization kind + GenericSignatureIDField, // specialized signature + DeclIDField, // target function + BCVBR<4>, // # of arguments (+1) or 1 if simple decl name, 0 if no target + BCVBR<4>, // # of SPI groups + BCVBR<4>, // # of availability attributes + BCArray // target function pieces, spi groups + >; using DifferentiableDeclAttrLayout = BCRecordLayout< Differentiable_DECL_ATTR, diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 0e96ea25cf8fd..49fc420137f4f 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -309,7 +309,8 @@ namespace sil_block { GenericSignatureIDField, // specialized signature DeclIDField, // Target SILFunction name or 0. DeclIDField, // SPIGroup or 0. - DeclIDField // SPIGroup Module name id. + DeclIDField, // SPIGroup Module name id. + BC_AVAIL_TUPLE // Availability >; // Has an optional argument list where each argument is a typed valueref. diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index c0dc41a69b1ae..92af4089fd863 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2593,12 +2593,16 @@ class Serializer::DeclSerializer : public DeclVisitor { auto numSPIGroups = attr->getSPIGroups().size(); assert(pieces.size() == numArgs + numSPIGroups || pieces.size() == (numArgs - 1 + numSPIGroups)); - + auto numAvailabilityAttrs = attr->getAvailabeAttrs().size(); SpecializeDeclAttrLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, (unsigned)attr->isExported(), (unsigned)attr->getSpecializationKind(), S.addGenericSignatureRef(attr->getSpecializedSignature()), - S.addDeclRef(targetFunDecl), numArgs, numSPIGroups, pieces); + S.addDeclRef(targetFunDecl), numArgs, numSPIGroups, + numAvailabilityAttrs, pieces); + for (auto availAttr : attr->getAvailabeAttrs()) { + writeDeclAttribute(D, availAttr); + } return; } @@ -3110,12 +3114,15 @@ class Serializer::DeclSerializer : public DeclVisitor { } void noteUseOfExportedPrespecialization(const AbstractFunctionDecl *afd) { + bool hasNoted = false; for (auto *A : afd->getAttrs().getAttributes()) { auto *SA = cast(A); if (!SA->isExported()) continue; if (auto *targetFunctionDecl = SA->getTargetFunctionDecl(afd)) { - exportedPrespecializationDecls.push_back(S.addDeclRef(afd)); + if (!hasNoted) + exportedPrespecializationDecls.push_back(S.addDeclRef(afd)); + hasNoted = true; } } } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index bceb228de858e..61391b095bb55 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -509,11 +509,18 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { spiGroupID = S.addUniquedStringRef(ident.str()); spiModuleDeclID = S.addModuleRef(SA->getSPIModule()); } + auto availability = SA->getAvailability(); + if (!availability.isAlwaysAvailable()) { + available = availability.getOSVersion().getLowerEndpoint(); + } + ENCODE_VER_TUPLE(available, available) + SILSpecializeAttrLayout::emitRecord( Out, ScratchRecord, specAttrAbbrCode, (unsigned)SA->isExported(), (unsigned)SA->getSpecializationKind(), S.addGenericSignatureRef(SA->getSpecializedSignature()), - targetFunctionNameID, spiGroupID, spiModuleDeclID); + targetFunctionNameID, spiGroupID, spiModuleDeclID, + LIST_VER_TUPLE_PIECES(available)); } // Assign a unique ID to each basic block of the SILFunction. diff --git a/test/ModuleInterface/availability-expansion.swift b/test/ModuleInterface/availability-expansion.swift index 7f42f1c14f3bb..39aa6d192ad0f 100644 --- a/test/ModuleInterface/availability-expansion.swift +++ b/test/ModuleInterface/availability-expansion.swift @@ -27,3 +27,8 @@ public func onMyProjectV1() {} public func onMyProjectV2_5() {} // CHECK: @available(macOS 10.12, *) // CHECK-NEXT: public func onMyProjectV2_5 + +@_specialize(exported: true, availability: SwiftStdlib 5.5, *; where T == Int) +public func testSemanticsAvailability(_ t: T) {} +// CHECK: @_specialize(exported: true, kind: full, availability: macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *; where T == Swift.Int) +// CHECK-NEXT: public func testSemanticsAvailability diff --git a/test/SIL/Parser/available.sil b/test/SIL/Parser/available.sil index 6a19b457a43c3..fb2a3e8c3f151 100644 --- a/test/SIL/Parser/available.sil +++ b/test/SIL/Parser/available.sil @@ -25,3 +25,11 @@ sil [available 123.321.444] @version_subminor : $@convention(thin) () -> () // CHECK-LABEL: sil [weak_imported] @weak_func : $@convention(thin) () -> () sil [weak_imported] @weak_func : $@convention(thin) () -> () + +public struct SomeData {} + +// CHECK-LABEL: sil [_specialize exported: true, kind: full, available: 10.50, where T == SomeData] @specialize_availability : $@convention(thin) (@in_guaranteed T) -> () +sil [_specialize exported: true, kind: full, available: 10.50, where T == SomeData] @specialize_availability : $@convention(thin) (@in_guaranteed T) -> () { +entry(%0 : $*T): + return undef: $() +} diff --git a/test/SIL/Serialization/available.sil b/test/SIL/Serialization/available.sil index f3c84c3338cc2..464c9ebbae9d6 100644 --- a/test/SIL/Serialization/available.sil +++ b/test/SIL/Serialization/available.sil @@ -7,6 +7,14 @@ import Builtin +public struct SomeData {} + +// CHECK-LABEL: sil [serialized] [_specialize exported: true, kind: full, available: 10.50, where T == SomeData] @specialize_availability : $@convention(thin) (@in_guaranteed T) -> () +sil [serialized] [_specialize exported: true, kind: full, available: 10.50, where T == SomeData] @specialize_availability : $@convention(thin) (@in_guaranteed T) -> () { +bb0(%0 : $*T): + return undef : $() +} + // CHECK-LABEL: sil [serialized] [weak_imported] @weak_func : $@convention(thin) () -> () sil [serialized] [weak_imported] @weak_func : $@convention(thin) () -> () { bb0: diff --git a/test/SILGen/Inputs/specialize_attr_module2.swift b/test/SILGen/Inputs/specialize_attr_module2.swift index 15449d17b50a9..0e3cfdef68613 100644 --- a/test/SILGen/Inputs/specialize_attr_module2.swift +++ b/test/SILGen/Inputs/specialize_attr_module2.swift @@ -2,6 +2,7 @@ import A extension PublicThing { @_specialize(exported: true, kind: full, target: doStuffWith(_:), where T == Int) + @_specialize(exported: true, kind: full, target: doStuffWith(_:), availability: macOS 10.50, *; where T == Int16) public func specializedoStuff2(_ t: T) {} } diff --git a/test/SILGen/availability_attribute.swift b/test/SILGen/availability_attribute.swift index cceb0ca07da9d..66fa4bad68cdd 100644 --- a/test/SILGen/availability_attribute.swift +++ b/test/SILGen/availability_attribute.swift @@ -34,3 +34,7 @@ public struct AvailableStruct { public func availableNestedMethod() {} } } + +@_specialize(exported: true, availability: macOS 10.50, iOS 13, *; where T == Int) +// CHECK-LABEL: sil [_specialize exported: true, kind: full, available: 10.50, where T == Int] [ossa] @$s22availability_attribute16testAvailabilityyyxlF +public func testAvailability(_ t: T) {} diff --git a/test/SILGen/specialize_attr.swift b/test/SILGen/specialize_attr.swift index dfc55cd4894ac..da6b20b090fb2 100644 --- a/test/SILGen/specialize_attr.swift +++ b/test/SILGen/specialize_attr.swift @@ -2,21 +2,21 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -module-name A -emit-module-path %t/A.swiftmodule -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module.swift // RUN: %target-swift-frontend -I %t -module-name B -emit-module-path %t/B.swiftmodule -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module2.swift -// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-%target-os // RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-EVO %s // Test .swiftinterface // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o /dev/null -module-name A -emit-module-interface-path %t/A.swiftinterface -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module.swift // RUN: %target-swift-frontend -emit-module -o /dev/null -I %t -module-name B -emit-module-interface-path %t/B.swiftinterface -enable-library-evolution -swift-version 5 %S/Inputs/specialize_attr_module2.swift -// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-%target-os // RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-EVO %s // Test .swiftmodule without library-evolution // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -module-name A -emit-module-path %t/A.swiftmodule -swift-version 5 %S/Inputs/specialize_attr_module.swift // RUN: %target-swift-frontend -I %t -module-name B -emit-module-path %t/B.swiftmodule -swift-version 5 %S/Inputs/specialize_attr_module2.swift -// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s +// RUN: %target-swift-emit-silgen -I %t -module-name specialize_attr -emit-verbose-sil %s -swift-version 5 | %FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-%target-os // RUN: %target-swift-emit-sil -I %t -sil-verify-all -O -module-name specialize_attr -emit-verbose-sil %s | %FileCheck -check-prefix=CHECK-OPT -check-prefix=CHECK-OPT-NOEVO %s import A @@ -124,7 +124,8 @@ public struct CC2 { } // Import from module A and B. -// CHECK-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int] @$s1A11PublicThingV11doStuffWithyyxF : $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, PublicThing<τ_0_0>) -> () +// CHECK-macosx-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, available: 10.50, where T == Int16] [_specialize exported: true, kind: full, where T == Int] @$s1A11PublicThingV11doStuffWithyyxF : $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, PublicThing<τ_0_0>) -> () +// CHECK-linux-gnu-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int16] [_specialize exported: true, kind: full, where T == Int] @$s1A11PublicThingV11doStuffWithyyxF : $@convention(method) <τ_0_0> (@in_guaranteed τ_0_0, PublicThing<τ_0_0>) -> () // CHECK-LABEL: sil [serialized] [_specialize exported: true, kind: full, where T == Double] [_specialize exported: true, kind: full, where T == Int] @$s1A13InternalThingV11doStuffWith5boxedyAA05BoxedB0VyxG_tF : $@convention(method) <τ_0_0> (BoxedThing<τ_0_0>, InternalThing<τ_0_0>) -> () // CHECK-DAG: sil [serialized] [_specialize exported: true, kind: full, where T == Int] @$s1A14InternalThing2V9computedZxvM : $@yield_once @convention(method) <τ_0_0> (@inout InternalThing2<τ_0_0>) -> @yields @inout τ_0_0 diff --git a/test/SILOptimizer/Inputs/pre_specialized_module.swift b/test/SILOptimizer/Inputs/pre_specialized_module.swift index 3eee2f982ad45..7dec0b9f32f23 100644 --- a/test/SILOptimizer/Inputs/pre_specialized_module.swift +++ b/test/SILOptimizer/Inputs/pre_specialized_module.swift @@ -1,8 +1,20 @@ +@frozen +public struct SomeData { + public init() {} +} + @_specialize(exported: true, where T == Int) @_specialize(exported: true, where T == Double) +@_specialize(exported: true, availability: macOS 10.50, *; where T == SomeData) public func publicPrespecialized(_ t: T) { } +@_specialize(exported: true, where T == Int) +@_specialize(exported: true, availability: macOS 10.50, *; where T == SomeData) +@inlinable +@inline(never) +public func publicPrespecialized2(_ t: T) { } + @_specialize(exported: true, where T == Int) @_specialize(exported: true, where T == Double) @_alwaysEmitIntoClient diff --git a/test/SILOptimizer/Inputs/pre_specialized_module2.swift b/test/SILOptimizer/Inputs/pre_specialized_module2.swift new file mode 100644 index 0000000000000..a32316f1b9f4b --- /dev/null +++ b/test/SILOptimizer/Inputs/pre_specialized_module2.swift @@ -0,0 +1,10 @@ +import pre_specialized_module + +@frozen +public struct SomeOtherData { + public init() {} +} + +@_specialize(exported: true, target: publicPrespecialized2(_:), availability: macOS 10.50, *; where T == SomeOtherData) +public func pre_specialize_publicPrespecialized(_ t: T) { +} diff --git a/test/SILOptimizer/pre_specialize-macos.swift b/test/SILOptimizer/pre_specialize-macos.swift new file mode 100644 index 0000000000000..501f046eb6c8c --- /dev/null +++ b/test/SILOptimizer/pre_specialize-macos.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -O -swift-version 5 -enable-library-evolution -emit-module -o /dev/null -emit-module-interface-path %t/pre_specialized_module.swiftinterface %S/Inputs/pre_specialized_module.swift -module-name pre_specialized_module +// RUN: %target-swift-frontend -I %t -O -swift-version 5 -enable-library-evolution -emit-module -o /dev/null -emit-module-interface-path %t/pre_specialized_module2.swiftinterface %S/Inputs/pre_specialized_module2.swift -module-name pre_specialized_module2 +// RUN: %target-swift-frontend -I %t -O -emit-sil -target x86_64-apple-macos11 %s | %FileCheck %s --check-prefix=OPT --check-prefix=CHECK +// RUN: %target-swift-frontend -I %t -O -emit-sil -target x86_64-apple-macosx10.9 %s | %FileCheck %s --check-prefix=NOOPT --check-prefix=CHECK + +// REQUIRES: OS=macosx + +import pre_specialized_module +import pre_specialized_module2 + +// OPT: sil [available 10.50] [noinline] @$s22pre_specialized_module21publicPrespecialized2yyxlFAA8SomeDataV_Ts5 : $@convention(thin) (SomeData) -> () +// OPT: sil [available 10.50] [noinline] @$s22pre_specialized_module21publicPrespecialized2yyxlF0a1_B8_module213SomeOtherDataV_Ts5 : $@convention(thin) (SomeOtherData) -> () + +// CHECK: sil @$s4main28usePrespecializedEntryPointsyyF : $@convention(thin) () -> () { +// OPT: [[F1:%.*]] = function_ref @$s22pre_specialized_module21publicPrespecialized2yyxlFAA8SomeDataV_Ts5 : $@convention(thin) (SomeData) -> () +// OPT: apply [[F1]]( +// OPT: [[F2:%.*]] = function_ref @$s22pre_specialized_module21publicPrespecialized2yyxlF0a1_B8_module213SomeOtherDataV_Ts5 : $@convention(thin) (SomeOtherData) -> () +// OPT: apply [[F2]]( +// In the no prespecialization case we get regular generic specialization +// because the function is inlinable. +// NOOPT: [[F1:%.*]] = function_ref @$s22pre_specialized_module21publicPrespecialized2yyxlFAA8SomeDataV_Tg5Tf4d_n : $@convention(thin) () -> () +// NOOPT: apply [[F1]]() +// NOOPT: [[F2:%.*]] = function_ref @$s22pre_specialized_module21publicPrespecialized2yyxlF0a1_B8_module213SomeOtherDataV_Tg5Tf4d_n : $@convention(thin) () -> () +// NOOPT: apply [[F2]]() +// CHECK: } // end sil function '$s4main28usePrespecializedEntryPointsyyF' +public func usePrespecializedEntryPoints() { + publicPrespecialized2(SomeData()) + publicPrespecialized2(SomeOtherData()) +} diff --git a/test/SILOptimizer/pre_specialize.swift b/test/SILOptimizer/pre_specialize.swift index 32eadcf33d85a..1eced39f4c2f6 100644 --- a/test/SILOptimizer/pre_specialize.swift +++ b/test/SILOptimizer/pre_specialize.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module-path %t/pre_specialized_module.swiftmodule %S/Inputs/pre_specialized_module.swift -// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT -// RUN: %target-swift-frontend -I %t -Onone -emit-sil %s | %FileCheck %s --check-prefix=NONE +// RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT -check-prefix=OPT-%target-os +// RUN: %target-swift-frontend -I %t -Onone -emit-sil %s | %FileCheck %s --check-prefix=NONE -check-prefix=NONE-%target-os // RUN: %empty-directory(%t) @@ -13,33 +13,41 @@ // RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -O -swift-version 5 -enable-library-evolution -emit-module -o /dev/null -emit-module-interface-path %t/pre_specialized_module.swiftinterface %S/Inputs/pre_specialized_module.swift +// RUN: %target-swift-frontend -O -swift-version 5 -enable-library-evolution -emit-module -o /dev/null -emit-module-interface-path %t/pre_specialized_module.swiftinterface %S/Inputs/pre_specialized_module.swift -module-name pre_specialized_module // RUN: %target-swift-frontend -I %t -O -emit-sil %s | %FileCheck %s --check-prefix=OPT import pre_specialized_module // Make sure we generate the public pre-specialized entry points. -// OPT: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { -// OPT: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { +// OPT-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// OPT-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { +// OPT-macosx-DAG: sil [available 10.5] @$s14pre_specialize10testPublic1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { +// OPT-linux-gnu-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { -// NONE: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { -// NONE: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { +// NONE-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// NONE-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { +// NONE-macosx-DAG: sil [available 10.5] @$s14pre_specialize10testPublic1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { +// NONE-linux-gnu-DAG: sil @$s14pre_specialize10testPublic1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { @_specialize(exported: true, where T == Int) @_specialize(exported: true, where T == Float) +@_specialize(exported: true, availability: macOS 10.5, *; where T == Double) public func testPublic(t: T) { print(t) } -// OPT: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { -// OPT: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { +// OPT-macosx-DAG: sil [available 10.5] @$s14pre_specialize18testEmitIntoClient1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { +// OPT-linux-gnu-DAG: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSd_Ts5 : $@convention(thin) (Double) -> () { +// OPT-DAG: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { +// OPT-DAG: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { // NONE: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSf_Ts5 : $@convention(thin) (Float) -> () { // NONE: sil @$s14pre_specialize18testEmitIntoClient1tyx_tlFSi_Ts5 : $@convention(thin) (Int) -> () { @_specialize(exported: true, where T == Int) @_specialize(exported: true, where T == Float) +@_specialize(exported: true, availability: macOS 10.5, *; where T == Double) @_alwaysEmitIntoClient internal func testEmitIntoClient(t: T) { print(t) @@ -50,6 +58,8 @@ internal func testEmitIntoClient(t: T) { // OPT: apply [[F1]] // OPT: [[F2:%.*]] = function_ref @$s22pre_specialized_module20publicPrespecializedyyxlFSd_Ts5 : $@convention(thin) (Double) -> () // OPT: apply [[F2]] +// OPT-macosx: [[F6:%.*]] = function_ref @$s22pre_specialized_module20publicPrespecializedyyxlF : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () +// OPT-macosx: apply [[F6]] // OPT: [[F3:%.*]] = function_ref @$s22pre_specialized_module36internalEmitIntoClientPrespecializedyyxlFSi_Ts5 : $@convention(thin) (Int) -> () // OPT: apply [[F3]] // OPT: [[F4:%.*]] = function_ref @$s22pre_specialized_module36internalEmitIntoClientPrespecializedyyxlFSd_Ts5 : $@convention(thin) (Double) -> () @@ -80,10 +90,19 @@ internal func testEmitIntoClient(t: T) { public func usePrespecializedEntryPoints() { publicPrespecialized(1) publicPrespecialized(1.0) + publicPrespecialized(SomeData()) useInternalEmitIntoClientPrespecialized(2) useInternalEmitIntoClientPrespecialized(2.0) useInternalThing(2) } +// OPT-macosx: sil [available 10.50] @$s14pre_specialize40usePrespecializedEntryPointsAvailabilityyyF : $@convention(thin) () -> () { +// OPT-macosx: [[F1:%.*]] = function_ref @$s22pre_specialized_module20publicPrespecializedyyxlFAA8SomeDataV_Ts5 : $@convention(thin) (SomeData) -> () +// OPT-macosx: apply [[F1]]( +// OPT-macosx: } // end sil function '$s14pre_specialize40usePrespecializedEntryPointsAvailabilityyyF' +@available(macOS 10.50, *) +public func usePrespecializedEntryPointsAvailability() { + publicPrespecialized(SomeData()) +} // OPT: sil [signature_optimized_thunk] [always_inline] @$s22pre_specialized_module16publicInlineableyyxlFSd_Ts5 : $@convention(thin) (Double) -> () { // NONE: sil @$s22pre_specialized_module16publicInlineableyyxlFSd_Ts5 : $@convention(thin) (Double) -> () { @_specialize(exported: true, target: publicInlineable(_:), where T == Double) diff --git a/test/attr/attr_specialize.swift b/test/attr/attr_specialize.swift index 32fe8e292b0ba..3f54fcdf04902 100644 --- a/test/attr/attr_specialize.swift +++ b/test/attr/attr_specialize.swift @@ -1,5 +1,5 @@ // RUN: %target-typecheck-verify-swift -// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename=%s -disable-objc-attr-requires-foundation-module | %FileCheck %s +// RUN: %target-swift-ide-test -print-ast-typechecked -source-filename=%s -disable-objc-attr-requires-foundation-module -define-availability 'SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' | %FileCheck %s struct S {} @@ -322,4 +322,24 @@ extension Container { // 'E == S.Element'. @_specialize(where S == Set) public func takesSequenceAndElement(_: S, _: E) - where S : Sequence, E == S.Element {} \ No newline at end of file + where S : Sequence, E == S.Element {} + +// CHECK: @_specialize(exported: true, kind: full, availability: macOS 11, iOS 13, *; where T == Int) +// CHECK: public func testAvailability(_ t: T) +@_specialize(exported: true, availability: macOS 11, iOS 13, *; where T == Int) +public func testAvailability(_ t: T) {} + +// CHECK: @_specialize(exported: true, kind: full, availability: macOS, introduced: 11; where T == Int) +// CHECK: public func testAvailability2(_ t: T) +@_specialize(exported: true, availability: macOS 11, *; where T == Int) +public func testAvailability2(_ t: T) {} + +// CHECK: @_specialize(exported: true, kind: full, availability: macOS, introduced: 11; where T == Int) +// CHECK: public func testAvailability3(_ t: T) +@_specialize(exported: true, availability: macOS, introduced: 11; where T == Int) +public func testAvailability3(_ t: T) {} + +// CHECK: @_specialize(exported: true, kind: full, availability: macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *; where T == Int) +// CHECK: public func testAvailability4(_ t: T) +@_specialize(exported: true, availability: SwiftStdlib 5.5, *; where T == Int) +public func testAvailability4(_ t: T) {} diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index d29e5d6afab93..41247167848e6 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -797,6 +797,11 @@ AllowCompilerErrors("allow-compiler-errors", llvm::cl::desc("Whether to attempt to continue despite compiler errors"), llvm::cl::init(false)); +static llvm::cl::opt + DefineAvailability("define-availability", + llvm::cl::desc("Define a macro for @available"), + llvm::cl::cat(Category)); + } // namespace options static std::unique_ptr @@ -3887,6 +3892,10 @@ int main(int argc, char *argv[]) { InitInvok.setModuleName(options::ModuleName); InitInvok.setSDKPath(options::SDK); + + if (!options::DefineAvailability.empty()) { + InitInvok.getLangOptions().AvailabilityMacros.push_back(options::DefineAvailability); + } InitInvok.getLangOptions().CollectParsedToken = true; InitInvok.getLangOptions().BuildSyntaxTree = true; InitInvok.getLangOptions().EnableCrossImportOverlays = diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 93f2c05edaf46..d135e3f5a4a93 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -102,10 +102,25 @@ element='Syntax', element_name='SpecializeAttribute', element_choices=[ 'LabeledSpecializeEntry', + 'AvailabilityEntry', 'TargetFunctionEntry', 'GenericWhereClause', ]), + Node('AvailabilityEntry', kind='Syntax', + description=''' + The availability argument for the _specialize attribute + ''', + children=[ + Child('Label', kind='IdentifierToken', + description='The label of the argument'), + Child('Colon', kind='ColonToken', + description='The colon separating the label and the value'), + Child('AvailabilityList', kind='AvailabilitySpecList', + collection_element_name='Availability'), + Child('Semicolon', kind='SemicolonToken'), + ]), + # Representation of e.g. 'exported: true,' # labeled-specialize-entry -> identifier ':' token ','? Node('LabeledSpecializeEntry', kind='Syntax', diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index fc445e62a313d..c3a4951b39361 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -253,6 +253,7 @@ 'TargetFunctionEntry': 248, 'PostfixIfConfigExpr': 250, 'UnavailabilityCondition': 251, + 'AvailabilityEntry' : 252, }