|
40 | 40 | #include "swift/AST/PropertyWrappers.h"
|
41 | 41 | #include "swift/AST/SourceFile.h"
|
42 | 42 | #include "swift/AST/StorageImpl.h"
|
| 43 | +#include "swift/AST/ASTPrinter.h" |
43 | 44 | #include "swift/AST/SwiftNameTranslation.h"
|
44 | 45 | #include "swift/AST/TypeCheckRequests.h"
|
45 | 46 | #include "swift/AST/Types.h"
|
@@ -6682,6 +6683,12 @@ static bool typeCheckDerivativeAttr(DerivativeAttr *attr) {
|
6682 | 6683 | // Note: Implementation must be idempotent because it may be called multiple
|
6683 | 6684 | // times for the same attribute.
|
6684 | 6685 | Decl *D = attr->getOriginalDeclaration();
|
| 6686 | + |
| 6687 | + // ABI-only decls can't have @derivative; bail out and let ABIDeclChecker |
| 6688 | + // diagnose this. |
| 6689 | + if (!ABIRoleInfo(D).providesAPI()) |
| 6690 | + return false; |
| 6691 | + |
6685 | 6692 | auto &Ctx = D->getASTContext();
|
6686 | 6693 | auto &diags = Ctx.Diags;
|
6687 | 6694 | // `@derivative` attribute requires experimental differentiable programming
|
@@ -9114,13 +9121,215 @@ class ABIDeclChecker : public ASTComparisonVisitor<ABIDeclChecker> {
|
9114 | 9121 |
|
9115 | 9122 | // MARK: @abi checking - attributes
|
9116 | 9123 |
|
| 9124 | + /// Are these attributes similar enough that they should be checked against |
| 9125 | + /// one another? At minimum this means they're of the same kind, but for some |
| 9126 | + /// attrs there are additional criteria. |
| 9127 | + bool canCompareAttrs(DeclAttribute *api, DeclAttribute *abi, |
| 9128 | + Decl *apiDecl, Decl *abiDecl) { |
| 9129 | + if (api->getKind() != abi->getKind()) |
| 9130 | + return false; |
| 9131 | + |
| 9132 | + auto getAvailableDomain = [](Decl *D, DeclAttribute *A) { |
| 9133 | + return D->getSemanticAvailableAttr(cast<AvailableAttr>(A))->getDomain(); |
| 9134 | + }; |
| 9135 | + |
| 9136 | + // Extra logic for specific attributes. |
| 9137 | + switch (api->getKind()) { |
| 9138 | + case DeclAttrKind::Expose: |
| 9139 | + return cast<ExposeAttr>(api)->getExposureKind() |
| 9140 | + == cast<ExposeAttr>(abi)->getExposureKind(); |
| 9141 | + |
| 9142 | + case DeclAttrKind::Extern: |
| 9143 | + return cast<ExternAttr>(api)->getExternKind() |
| 9144 | + == cast<ExternAttr>(abi)->getExternKind(); |
| 9145 | + |
| 9146 | + case DeclAttrKind::Available: |
| 9147 | + return getAvailableDomain(apiDecl, api) |
| 9148 | + == getAvailableDomain(abiDecl, abi); |
| 9149 | + return true; |
| 9150 | + |
| 9151 | + default: |
| 9152 | + break; |
| 9153 | + } |
| 9154 | + |
| 9155 | + return true; |
| 9156 | + } |
| 9157 | + |
| 9158 | + /// Check two attribute lists against one another. |
| 9159 | + /// |
| 9160 | + /// This pairs up attributes which are sufficiently similar (as determined by |
| 9161 | + /// \c canCompareAttrs() ) and then checks them. Attributes which |
| 9162 | + /// have no counterpart are checked individually. |
9117 | 9163 | bool checkAttrs(DeclAttributes api, DeclAttributes abi,
|
9118 | 9164 | Decl *apiDecl, Decl *abiDecl) {
|
9119 | 9165 | bool didDiagnose = false;
|
9120 |
| - // TODO: Actually check attributes |
| 9166 | + |
| 9167 | + // Collect all ABI attrs. |
| 9168 | + SmallVector<DeclAttribute*, 32> remainingABIDeclAttrs; |
| 9169 | + for (auto *abiDeclAttr : abi) { |
| 9170 | + remainingABIDeclAttrs.push_back(abiDeclAttr); |
| 9171 | + } |
| 9172 | + |
| 9173 | + // Visit each API attr, pairing it with an ABI attr if possible. |
| 9174 | + // Note that this will visit even invalid attributes. |
| 9175 | + for (auto *apiDeclAttr : api) { |
| 9176 | + auto abiAttrIter = llvm::find_if(remainingABIDeclAttrs, |
| 9177 | + [&](DeclAttribute *abiDeclAttr) { |
| 9178 | + return abiDeclAttr && canCompareAttrs(apiDeclAttr, abiDeclAttr, |
| 9179 | + apiDecl, abiDecl); |
| 9180 | + }); |
| 9181 | + DeclAttribute *abiDeclAttr = nullptr; |
| 9182 | + if (abiAttrIter != remainingABIDeclAttrs.end()) { |
| 9183 | + // Found a matching ABI attr. Claim and use it. |
| 9184 | + std::swap(abiDeclAttr, *abiAttrIter); |
| 9185 | + } |
| 9186 | + didDiagnose |= checkAttr(apiDeclAttr, abiDeclAttr, apiDecl, abiDecl); |
| 9187 | + } |
| 9188 | + |
| 9189 | + // Visit leftover ABI attrs. |
| 9190 | + for (auto *abiDeclAttr : remainingABIDeclAttrs) { |
| 9191 | + if (abiDeclAttr) |
| 9192 | + didDiagnose |= checkAttr(nullptr, abiDeclAttr, apiDecl, abiDecl); |
| 9193 | + } |
9121 | 9194 | return didDiagnose;
|
9122 | 9195 | }
|
9123 | 9196 |
|
| 9197 | + /// Check a single attribute against its counterpart. If an attribute has no |
| 9198 | + /// counterpart, the counterpart may be \c nullptr ; either \p abi or \p abi |
| 9199 | + /// may be \c nullptr , but never both. |
| 9200 | + bool checkAttr(DeclAttribute *api, DeclAttribute *abi, |
| 9201 | + Decl *apiDecl, Decl *abiDecl) { |
| 9202 | + ASSERT(api || abi && "checkAttr() should have at least one attribute"); |
| 9203 | + |
| 9204 | + // If either attribute has already been diagnosed, don't check here. |
| 9205 | + if ((api && api->isInvalid()) || (abi && abi->isInvalid())) |
| 9206 | + return true; |
| 9207 | + |
| 9208 | + auto kind = api ? api->getKind() : abi->getKind(); |
| 9209 | + auto behaviors = DeclAttribute::getBehaviors(kind); |
| 9210 | + |
| 9211 | + switch (behaviors & DeclAttribute::InABIAttrMask) { |
| 9212 | + case DeclAttribute::UnreachableInABIAttr: |
| 9213 | + ASSERT(abiAttr->canAppearOnDecl(apiDecl) |
| 9214 | + && "checking @abi on decl that can't have it???"); |
| 9215 | + ASSERT(!abiAttr->canAppearOnDecl(apiDecl) |
| 9216 | + && "unreachable-in-@abi attr on reachable decl???"); |
| 9217 | + |
| 9218 | + // If the asserts are disabled, fall through to no checking. |
| 9219 | + LLVM_FALLTHROUGH; |
| 9220 | + |
| 9221 | + case DeclAttribute::UnconstrainedInABIAttr: |
| 9222 | + // No checking required. |
| 9223 | + return false; |
| 9224 | + |
| 9225 | + case DeclAttribute::ForbiddenInABIAttr: |
| 9226 | + // Diagnose if ABI has attribute. |
| 9227 | + if (abi) { |
| 9228 | + // A shorthand `@available(foo 1, bar 2, *)` attribute gets parsed into |
| 9229 | + // several separate `AvailableAttr`s, each with the full range of the |
| 9230 | + // shorthand attribute. If we've already diagnosed one of them, don't |
| 9231 | + // diagnose the rest; otherwise, record that we've diagnosed this one. |
| 9232 | + if (isa<AvailableAttr>(abi) && |
| 9233 | + !diagnosedAvailableAttrSourceLocs.insert(abi->getLocation())) |
| 9234 | + return true; |
| 9235 | + |
| 9236 | + diagnoseAndRemoveAttr(abi, diag::attr_abi_forbidden_attr, |
| 9237 | + abi->getAttrName(), abi->isDeclModifier()); |
| 9238 | + return true; |
| 9239 | + } |
| 9240 | + |
| 9241 | + return false; |
| 9242 | + |
| 9243 | + case DeclAttribute::InferredInABIAttr: |
| 9244 | + if (!abi && api->canClone()) { |
| 9245 | + // Infer an identical attribute. |
| 9246 | + abi = api->clone(ctx); |
| 9247 | + abi->setImplicit(true); |
| 9248 | + abiDecl->getAttrs().add(abi); |
| 9249 | + |
| 9250 | + if (ctx.LangOpts.EnableABIInferenceRemarks) { |
| 9251 | + SmallString<64> scratch; |
| 9252 | + auto abiAttrAsString = printAttr(abi, abiDecl, scratch); |
| 9253 | + |
| 9254 | + abiDecl->diagnose(diag::abi_attr_inferred_attribute, |
| 9255 | + abiAttrAsString, api->isDeclModifier()); |
| 9256 | + noteAttrHere(api, apiDecl, /*isMatch=*/true); |
| 9257 | + } |
| 9258 | + } |
| 9259 | + |
| 9260 | + // Other than the cloning behavior, Inferred behaves like Equivalent. |
| 9261 | + LLVM_FALLTHROUGH; |
| 9262 | + |
| 9263 | + case DeclAttribute::EquivalentInABIAttr: |
| 9264 | + // Diagnose if API doesn't have attribute. |
| 9265 | + if (!api) { |
| 9266 | + diagnoseAndRemoveAttr(abi, diag::attr_abi_extra_attr, |
| 9267 | + abi->getAttrName(), abi->isDeclModifier(), |
| 9268 | + abi->isImplicit()); |
| 9269 | + return true; |
| 9270 | + } |
| 9271 | + |
| 9272 | + // Diagnose if ABI doesn't have attribute. |
| 9273 | + if (!abi) { |
| 9274 | + SmallString<64> scratch; |
| 9275 | + auto apiAttrAsString = printAttr(api, apiDecl, scratch, |
| 9276 | + /*toInsert=*/true); |
| 9277 | + |
| 9278 | + ctx.Diags.diagnose(abiDecl, diag::attr_abi_missing_attr, |
| 9279 | + api->getAttrName(), api->isDeclModifier()) |
| 9280 | + .fixItInsert(abiDecl->getAttributeInsertionLoc(api->isDeclModifier()), |
| 9281 | + apiAttrAsString); |
| 9282 | + noteAttrHere(api, apiDecl); |
| 9283 | + return true; |
| 9284 | + } |
| 9285 | + |
| 9286 | + // Diagnose if two attributes are mismatched. |
| 9287 | + if (!api->isEquivalent(abi, apiDecl)) { |
| 9288 | + SmallString<64> scratch; |
| 9289 | + auto apiAttrAsString = printAttr(api, apiDecl, scratch); |
| 9290 | + |
| 9291 | + ctx.Diags.diagnose(abi->getLocation(), diag::attr_abi_mismatched_attr, |
| 9292 | + abi->getAttrName(), abi->isDeclModifier(), |
| 9293 | + apiAttrAsString) |
| 9294 | + .fixItReplace(abi->getRangeWithAt(), apiAttrAsString); |
| 9295 | + noteAttrHere(api, apiDecl); |
| 9296 | + return true; |
| 9297 | + } |
| 9298 | + |
| 9299 | + return false; |
| 9300 | + } |
| 9301 | + |
| 9302 | + llvm_unreachable("unknown InABIAttrMask behavior"); |
| 9303 | + } |
| 9304 | + |
| 9305 | + StringRef printAttr(DeclAttribute *attr, |
| 9306 | + Decl *decl, |
| 9307 | + SmallVectorImpl<char> &scratch, |
| 9308 | + bool toInsert = false) { |
| 9309 | + auto opts = PrintOptions::printForDiagnostics(AccessLevel::Private, |
| 9310 | + ctx.TypeCheckerOpts.PrintFullConvention); |
| 9311 | + opts.PrintLongAttrsOnSeparateLines = false; |
| 9312 | + |
| 9313 | + llvm::raw_svector_ostream os{scratch}; |
| 9314 | + StreamPrinter printer{os}; |
| 9315 | + attr->print(printer, opts, decl); |
| 9316 | + |
| 9317 | + auto str = StringRef(scratch.begin(), scratch.size()); |
| 9318 | + if (!toInsert) |
| 9319 | + str = str.trim(' '); |
| 9320 | + return str; |
| 9321 | + } |
| 9322 | + |
| 9323 | + void noteAttrHere(DeclAttribute *attr, Decl *decl, bool isMatch = false) { |
| 9324 | + SourceLoc loc = attr->getLocation(); |
| 9325 | + if (loc.isValid()) |
| 9326 | + ctx.Diags.diagnose(loc, diag::attr_abi_matching_attr_here, |
| 9327 | + isMatch, attr->isDeclModifier(), attr->isImplicit()); |
| 9328 | + else |
| 9329 | + ctx.Diags.diagnose(decl, diag::attr_abi_matching_attr_here, |
| 9330 | + isMatch, attr->isDeclModifier(), attr->isImplicit()); |
| 9331 | + } |
| 9332 | + |
9124 | 9333 | // MARK: @abi checking - types
|
9125 | 9334 |
|
9126 | 9335 | bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc) {
|
|
0 commit comments