Skip to content

Commit 91d1d2e

Browse files
authored
Merge pull request #85793 from susmonteiro/susmonteiro/diagnose-swift-attrs
[cxx-interop] Diagnose invalid swift attributes
2 parents 125a63d + fb5bc72 commit 91d1d2e

11 files changed

+317
-90
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,6 @@ GROUPED_WARNING(import_multiple_mainactor_attr, ClangDeclarationImport, none,
120120
"this attribute for global actor '%0' is invalid; the declaration already has attribute for global actor '%1'",
121121
(StringRef, StringRef))
122122

123-
GROUPED_WARNING(contradicting_mutation_attrs, ClangDeclarationImport, none,
124-
"attribute '%0' is ignored when combined with attribute '%1'",
125-
(StringRef, StringRef))
126-
127123
GROUPED_WARNING(nonmutating_without_const, ClangDeclarationImport, none,
128124
"attribute 'nonmutating' has no effect on non-const method", ())
129125

@@ -199,10 +195,10 @@ NOTE(projection_reference_not_imported, none, "C++ method '%0' that returns a re
199195
NOTE(projection_may_return_interior_ptr, none, "C++ method '%0' may return an "
200196
"interior pointer",
201197
(StringRef))
202-
NOTE(mark_self_contained, none, "annotate type '%0' with 'SWIFT_SELF_CONTAINED' in C++ to "
198+
NOTE(mark_self_contained, none, "annotate type '%0' with SWIFT_SELF_CONTAINED in C++ to "
203199
"make methods that return it available in Swift",
204200
(StringRef))
205-
NOTE(mark_safe_to_import, none, "annotate method '%0' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to "
201+
NOTE(mark_safe_to_import, none, "annotate method '%0' with SWIFT_RETURNS_INDEPENDENT_VALUE in C++ to "
206202
"make it available in Swift",
207203
(StringRef))
208204

@@ -231,8 +227,6 @@ ERROR(private_fileid_attr_repeated, none,
231227
ERROR(private_fileid_attr_on_incomplete_type, none,
232228
"SWIFT_PRIVATE_FILEID cannot be applied to incomplete type, '%0'",
233229
(StringRef))
234-
NOTE(private_fileid_attr_here, none,
235-
"SWIFT_PRIVATE_FILEID annotation found here", ())
236230

237231
GROUPED_WARNING(private_fileid_attr_format_invalid, ClangDeclarationImport, none,
238232
"SWIFT_PRIVATE_FILEID annotation on '%0' does not have a valid file ID",
@@ -414,6 +408,18 @@ NOTE(
414408
"SWIFT_REFCOUNTED_PTR on %1 has incorrect signature; expected a function "
415409
"that takes no arguments and returns a pointer to a foreign reference type",
416410
(const Decl *, const clang::NamedDecl *))
411+
ERROR(invalid_conditional_attr, none,
412+
"%0 is invalid because it is only supported in "
413+
"class templates",
414+
(StringRef))
415+
416+
ERROR(conflicting_swift_attrs, none,
417+
"multiple conflicting annotations found on %0", (const clang::NamedDecl *))
418+
419+
ERROR(repeating_swift_attrs, none, "multiple %0 annotations found on %1",
420+
(StringRef, const clang::NamedDecl *))
421+
422+
NOTE(annotation_here, none, "%0 annotation found here", (StringRef))
417423

418424
#define UNDEFINE_DIAGNOSTIC_MACROS
419425
#include "DefineDiagnosticMacros.h"

include/swift/AST/DiagnosticsIRGen.def

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,24 +96,24 @@ NOTE(use_requires_expression, none,
9696
())
9797

9898
NOTE(annotate_copyable_if, none,
99-
"annotate a type with 'SWIFT_COPYABLE_IF(<T>)' in C++ to specify "
99+
"annotate a type with SWIFT_COPYABLE_IF(<T>) in C++ to specify "
100100
"that the type is Copyable if <T> is Copyable",
101101
())
102102

103103
NOTE(annotate_non_copyable, none,
104-
"annotate a type with 'SWIFT_NONCOPYABLE' in C++ to import it as "
104+
"annotate a type with SWIFT_NONCOPYABLE in C++ to import it as "
105105
"~Copyable",
106106
())
107107

108108
NOTE(maybe_missing_annotation, none,
109109
"one of the types that %0 depends on may need a 'requires' clause (since "
110-
"C++20) in the copy constructor, a 'SWIFT_COPYABLE_IF' annotation or a "
111-
"'SWIFT_NONCOPYABLE' annotation'",
110+
"C++20) in the copy constructor, a SWIFT_COPYABLE_IF annotation or a "
111+
"SWIFT_NONCOPYABLE annotation'",
112112
(const clang::NamedDecl *))
113113

114114
NOTE(maybe_missing_parameter, none,
115115
"the %select{'requires' clause on the copy constructor "
116-
"of|'SWIFT_COPYABLE_IF' annotation on}0 %1 may be missing a "
116+
"of|SWIFT_COPYABLE_IF annotation on}0 %1 may be missing a "
117117
"%select{constraint|parameter}0",
118118
(bool, const clang::NamedDecl *))
119119

lib/ClangImporter/ClangImporter.cpp

Lines changed: 108 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5455,28 +5455,34 @@ static bool checkConditionalParams(
54555455
}
54565456

54575457
static std::set<StringRef>
5458-
getConditionalAttrParams(const clang::RecordDecl *decl, StringRef attrName) {
5458+
getConditionalAttrParams(clang::SwiftAttrAttr *swiftAttr, StringRef attrName) {
54595459
std::set<StringRef> result;
5460-
if (!decl->hasAttrs())
5461-
return result;
5462-
for (auto attr : decl->getAttrs()) {
5463-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr)) {
5464-
StringRef params = swiftAttr->getAttribute();
5465-
if (params.consume_front(attrName)) {
5466-
auto commaPos = params.find(',');
5467-
StringRef nextParam = params.take_front(commaPos);
5468-
while (!nextParam.empty() && commaPos != StringRef::npos) {
5469-
result.insert(nextParam.trim());
5470-
params = params.drop_front(nextParam.size() + 1);
5471-
commaPos = params.find(',');
5472-
nextParam = params.take_front(commaPos);
5473-
}
5474-
}
5460+
StringRef params = swiftAttr->getAttribute();
5461+
if (params.consume_front(attrName)) {
5462+
auto commaPos = params.find(',');
5463+
StringRef nextParam = params.take_front(commaPos);
5464+
while (!nextParam.empty() && commaPos != StringRef::npos) {
5465+
result.insert(nextParam.trim());
5466+
params = params.drop_front(nextParam.size() + 1);
5467+
commaPos = params.find(',');
5468+
nextParam = params.take_front(commaPos);
54755469
}
54765470
}
54775471
return result;
54785472
}
54795473

5474+
static std::set<StringRef>
5475+
getConditionalAttrParams(const clang::NamedDecl *decl, StringRef attrName) {
5476+
if (decl->hasAttrs()) {
5477+
for (auto attr : decl->getAttrs()) {
5478+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
5479+
return getConditionalAttrParams(swiftAttr, attrName);
5480+
}
5481+
}
5482+
5483+
return {};
5484+
}
5485+
54805486
static std::set<StringRef>
54815487
getConditionalEscapableAttrParams(const clang::RecordDecl *decl) {
54825488
return getConditionalAttrParams(decl, "escapable_if:");
@@ -5509,15 +5515,6 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
55095515
// escapability annotations, malformed escapability annotations)
55105516

55115517
bool hasUnknown = false;
5512-
auto desugared = desc.type->getUnqualifiedDesugaredType();
5513-
if (const auto *recordType = desugared->getAs<clang::RecordType>()) {
5514-
auto recordDecl = recordType->getDecl();
5515-
// If the root type has a SWIFT_ESCAPABLE annotation, we import the type as
5516-
// Escapable without entering the loop
5517-
if (hasEscapableAttr(recordDecl))
5518-
return CxxEscapability::Escapable;
5519-
}
5520-
55215518
llvm::SmallVector<const clang::Type *, 4> stack;
55225519
// Keep track of Types we've seen to avoid cycles
55235520
llvm::SmallDenseSet<const clang::Type *, 4> seen;
@@ -5551,13 +5548,6 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
55515548
hasUnknown &= checkConditionalParams<CxxEscapability>(
55525549
recordDecl, desc.impl, STLParams, conditionalParams,
55535550
maybePushToStack);
5554-
5555-
if (desc.impl) {
5556-
HeaderLoc loc{recordDecl->getLocation()};
5557-
for (auto name : conditionalParams)
5558-
desc.impl->diagnose(loc, diag::unknown_template_parameter, name);
5559-
}
5560-
55615551
continue;
55625552
}
55635553
// Only try to infer escapability if the record doesn't have any
@@ -8593,16 +8583,12 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
85938583
auto conditionalParams = getConditionalCopyableAttrParams(recordDecl);
85948584

85958585
if (!STLParams.empty() || !conditionalParams.empty()) {
8596-
hasUnknown &= checkConditionalParams<CxxValueSemanticsKind>(
8597-
recordDecl, importerImpl, STLParams, conditionalParams,
8598-
maybePushToStack);
8599-
8600-
if (importerImpl) {
8601-
HeaderLoc loc{recordDecl->getLocation()};
8602-
for (auto name : conditionalParams)
8603-
importerImpl->diagnose(loc, diag::unknown_template_parameter, name);
8586+
if (isa<clang::ClassTemplateSpecializationDecl>(recordDecl) &&
8587+
importerImpl) {
8588+
hasUnknown &= checkConditionalParams<CxxValueSemanticsKind>(
8589+
recordDecl, importerImpl, STLParams, conditionalParams,
8590+
maybePushToStack);
86048591
}
8605-
86068592
continue;
86078593
}
86088594
}
@@ -8636,7 +8622,8 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
86368622
if (!hasDestroyTypeOperations(cxxRecordDecl) ||
86378623
(!isCopyable && !isMovable)) {
86388624

8639-
if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl))
8625+
if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl) &&
8626+
importerImpl)
86408627
importerImpl->addImportDiagnostic(
86418628
cxxRecordDecl, Diagnostic(diag::record_unsupported_default_args),
86428629
cxxRecordDecl->getLocation());
@@ -9263,6 +9250,85 @@ swift::importer::getCxxRefConventionWithAttrs(const clang::Decl *decl) {
92639250
return std::nullopt;
92649251
}
92659252

9253+
static const std::vector<std::vector<std::pair<StringRef, StringRef>>>
9254+
ConflictingSwiftAttrs = {
9255+
{{"Escapable", "SWIFT_ESCAPABLE"},
9256+
{"~Escapable", "SWIFT_NONESCAPABLE"},
9257+
{"escapable_if:", "SWIFT_ESCAPABLE_IF"}},
9258+
{{"~Copyable", "SWIFT_NONCOPYABLE"},
9259+
{"copyable_if:", "SWIFT_COPYABLE_IF"}},
9260+
{{"mutating", "SWIFT_MUTATING"}, {"nonmutating", "'nonmutating'"}}};
9261+
9262+
static bool isConditionalAttr(StringRef attrName) {
9263+
return attrName == "escapable_if:" || attrName == "copyable_if:";
9264+
}
9265+
9266+
void ClangImporter::Implementation::validateSwiftAttributes(
9267+
const clang::NamedDecl *decl) {
9268+
if (!decl->hasAttrs())
9269+
return;
9270+
9271+
for (const auto &groupOfAttrs : ConflictingSwiftAttrs) {
9272+
// stores this decl's attributes, grouped by annotation kind
9273+
llvm::StringMap<std::vector<clang::SwiftAttrAttr *>> found;
9274+
unsigned count = 0;
9275+
for (auto [attrName, annotationName] : groupOfAttrs) {
9276+
for (auto attr : decl->getAttrs()) {
9277+
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
9278+
if (swiftAttr && swiftAttr->getAttribute().starts_with(attrName)) {
9279+
found[annotationName].push_back(swiftAttr);
9280+
count += 1;
9281+
9282+
// in the common case, we validate at most one conditional attribute
9283+
if (isConditionalAttr(attrName)) {
9284+
auto specDecl =
9285+
dyn_cast<clang::ClassTemplateSpecializationDecl>(decl);
9286+
if (!specDecl) {
9287+
diagnose(HeaderLoc{swiftAttr->getLocation()},
9288+
diag::invalid_conditional_attr, annotationName);
9289+
continue;
9290+
}
9291+
auto conditionalParams =
9292+
getConditionalAttrParams(swiftAttr, attrName);
9293+
while (specDecl && !conditionalParams.empty()) {
9294+
auto templateDecl = specDecl->getSpecializedTemplate();
9295+
for (auto [idx, param] :
9296+
llvm::enumerate(*templateDecl->getTemplateParameters()))
9297+
conditionalParams.erase(param->getName());
9298+
9299+
const clang::DeclContext *dc = specDecl;
9300+
specDecl = nullptr;
9301+
while ((dc = dc->getParent())) {
9302+
specDecl = dyn_cast<clang::ClassTemplateSpecializationDecl>(dc);
9303+
if (specDecl)
9304+
break;
9305+
}
9306+
}
9307+
for (auto name : conditionalParams)
9308+
diagnose(HeaderLoc{decl->getLocation()},
9309+
diag::unknown_template_parameter, name);
9310+
}
9311+
}
9312+
}
9313+
}
9314+
9315+
if (count > 1) {
9316+
if (found.size() == 1)
9317+
diagnose(HeaderLoc{decl->getLocation()}, diag::repeating_swift_attrs,
9318+
found.begin()->getKey(), decl);
9319+
else
9320+
diagnose(HeaderLoc{decl->getLocation()}, diag::conflicting_swift_attrs,
9321+
decl);
9322+
9323+
for (auto &[annotationName, attrs] : found) {
9324+
for (auto attr : attrs)
9325+
diagnose(HeaderLoc{attr->getLocation()}, diag::annotation_here,
9326+
annotationName);
9327+
}
9328+
}
9329+
}
9330+
}
9331+
92669332
NominalType *swift::stripInlineNamespaces(NominalType *outer,
92679333
NominalType *inner) {
92689334
if (!outer || !inner || inner == outer)

lib/ClangImporter/ImportDecl.cpp

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2355,6 +2355,7 @@ namespace {
23552355
if (alreadyImportedResult != Impl.ImportedDecls.end())
23562356
return alreadyImportedResult->second;
23572357

2358+
Impl.validateSwiftAttributes(decl);
23582359
auto loc = Impl.importSourceLoc(decl->getLocation());
23592360
if (recordHasReferenceSemantics(decl))
23602361
result = Impl.createDeclWithClangNode<ClassDecl>(
@@ -2837,7 +2838,8 @@ namespace {
28372838
Impl.diagnose(HeaderLoc(decl->getLocation()),
28382839
diag::private_fileid_attr_repeated, decl->getName());
28392840
for (auto ann : anns)
2840-
Impl.diagnose(HeaderLoc(ann.second), diag::private_fileid_attr_here);
2841+
Impl.diagnose(HeaderLoc(ann.second), diag::annotation_here,
2842+
"SWIFT_PRIVATE_FILEID");
28412843
} else if (anns.size() == 1) {
28422844
auto ann = anns[0];
28432845
if (!SourceFile::FileIDStr::parse(ann.first)) {
@@ -3123,8 +3125,8 @@ namespace {
31233125
diag::private_fileid_attr_on_incomplete_type,
31243126
decl->getName());
31253127
for (auto attr : attrs)
3126-
Impl.diagnose(HeaderLoc(attr.second),
3127-
diag::private_fileid_attr_here);
3128+
Impl.diagnose(HeaderLoc(attr.second), diag::annotation_here,
3129+
"SWIFT_PRIVATE_FILEID");
31283130
}
31293131

31303132
forwardDeclaration = true;
@@ -3651,6 +3653,7 @@ namespace {
36513653
}
36523654

36533655
checkBridgingAttrs(decl);
3656+
Impl.validateSwiftAttributes(decl);
36543657

36553658
return importFunctionDecl(decl, importedName, correctSwiftName,
36563659
std::nullopt);
@@ -9003,7 +9006,6 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90039006
ClangDecl = cast<clang::NamedDecl>(maybeDefinition.value());
90049007

90059008
std::optional<const clang::SwiftAttrAttr *> seenMainActorAttr;
9006-
const clang::SwiftAttrAttr *seenMutabilityAttr = nullptr;
90079009
llvm::SmallSet<ProtocolDecl *, 4> conformancesSeen;
90089010
const clang::SwiftAttrAttr *seenSendableSuppressionAttr = nullptr;
90099011

@@ -9054,19 +9056,6 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
90549056
}
90559057
}
90569058
}
9057-
9058-
// Check for contradicting mutability attr
9059-
if (seenMutabilityAttr) {
9060-
StringRef previous = seenMutabilityAttr->getAttribute();
9061-
9062-
if (previous != attr) {
9063-
diagnose(HeaderLoc(swiftAttr->getLocation()),
9064-
diag::contradicting_mutation_attrs, attr, previous);
9065-
continue;
9066-
}
9067-
}
9068-
9069-
seenMutabilityAttr = swiftAttr;
90709059
}
90719060

90729061
// Hard-code @actorIndependent, until Objective-C clients start

lib/ClangImporter/ImporterImpl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,12 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11281128
Type applyImportTypeAttrs(ImportTypeAttrs attrs, Type type,
11291129
llvm::function_ref<void(Diagnostic &&)> addImportDiagnosticFn);
11301130

1131+
/// Determines whether the given Clang declaration has conflicting
1132+
/// Swift attributes and emits diagnostics for any violations found.
1133+
///
1134+
/// \param decl The Clang record or function declaration to validate.
1135+
void validateSwiftAttributes(const clang::NamedDecl *decl);
1136+
11311137
/// If we already imported a given decl, return the corresponding Swift decl.
11321138
/// Otherwise, return nullptr.
11331139
std::optional<Decl *> importDeclCached(const clang::NamedDecl *ClangDecl,

test/Interop/Cxx/class/Inputs/mutability-annotations.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,16 @@ struct HasMutableProperty {
2525

2626
int noAnnotation() const { return b; }
2727

28-
// expected-warning@+1 {{attribute 'mutating' is ignored when combined with attribute 'nonmutating'}}
28+
// expected-error@+3 {{multiple conflicting annotations found on 'contradictingAnnotations'}}
29+
// expected-note@+2 {{'nonmutating' annotation found here}}
30+
// expected-note@+1 {{SWIFT_MUTATING annotation found here}}
2931
int contradictingAnnotations() const __attribute__((__swift_attr__("nonmutating"))) __attribute__((__swift_attr__("mutating"))) {
3032
return b;
3133
}
3234

35+
// expected-error@+3 {{multiple 'nonmutating' annotations found on 'duplicateAnnotations'}}
36+
// expected-note@+2 {{'nonmutating' annotation found here}}
37+
// expected-note@+1 {{'nonmutating' annotation found here}}
3338
int duplicateAnnotations() const __attribute__((__swift_attr__("nonmutating"))) __attribute__((__swift_attr__("nonmutating"))) {
3439
return b;
3540
}

test/Interop/Cxx/class/fixit-add-safe-to-import-self-contained.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ struct X {
2323
import Test
2424

2525
public func test(x: X) {
26-
// CHECK: note: annotate method 'test' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
26+
// CHECK: note: annotate method 'test' with SWIFT_RETURNS_INDEPENDENT_VALUE in C++ to make it available in Swift
2727
// CHECK: int *test() { }
2828
// CHECK: ^
2929
// CHECK: SWIFT_RETURNS_INDEPENDENT_VALUE
3030

3131
x.test()
3232

33-
// CHECK: note: annotate method 'other' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
33+
// CHECK: note: annotate method 'other' with SWIFT_RETURNS_INDEPENDENT_VALUE in C++ to make it available in Swift
3434
// CHECK: Ptr other() { }
3535
// CHECK: ^
3636
// CHECK: SWIFT_RETURNS_INDEPENDENT_VALUE
3737

38-
// CHECK: note: annotate type 'Ptr' with 'SWIFT_SELF_CONTAINED' in C++ to make methods that return it available in Swift
38+
// CHECK: note: annotate type 'Ptr' with SWIFT_SELF_CONTAINED in C++ to make methods that return it available in Swift
3939
// CHECK: struct Ptr {
4040
// CHECK: ^
4141
// CHECK: SWIFT_SELF_CONTAINED

0 commit comments

Comments
 (0)