Use ARC parsing rules for ns_returns_retained in MRC so that code can

be shared without warnings.  Build AttributedTypes to leave breadcrumbs
for tools like the static analyzer.  Warn about attempting to use the
attribute with incompatible return types.

llvm-svn: 308092
This commit is contained in:
John McCall 2017-07-15 11:06:46 +00:00
parent 8561c2e22c
commit 12251887bd
9 changed files with 107 additions and 47 deletions

View File

@ -3878,6 +3878,7 @@ public:
attr_sptr,
attr_uptr,
attr_nonnull,
attr_ns_returns_retained,
attr_nullable,
attr_null_unspecified,
attr_objc_kindof,

View File

@ -8380,6 +8380,8 @@ public:
unsigned SpellingListIndex, bool isNSConsumed,
bool isTemplateInstantiation);
bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type);
//===--------------------------------------------------------------------===//
// C++ Coroutines TS
//

View File

@ -3023,6 +3023,7 @@ bool AttributedType::isQualifier() const {
case AttributedType::attr_sptr:
case AttributedType::attr_uptr:
case AttributedType::attr_objc_kindof:
case AttributedType::attr_ns_returns_retained:
return false;
}
llvm_unreachable("bad attributed type kind");
@ -3056,6 +3057,7 @@ bool AttributedType::isCallingConv() const {
case attr_objc_inert_unsafe_unretained:
case attr_noreturn:
case attr_nonnull:
case attr_ns_returns_retained:
case attr_nullable:
case attr_null_unspecified:
case attr_objc_kindof:

View File

@ -104,6 +104,7 @@ namespace {
void printAfter(QualType T, raw_ostream &OS);
void AppendScope(DeclContext *DC, raw_ostream &OS);
void printTag(TagDecl *T, raw_ostream &OS);
void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS);
#define ABSTRACT_TYPE(CLASS, PARENT)
#define TYPE(CLASS, PARENT) \
void print##CLASS##Before(const CLASS##Type *T, raw_ostream &OS); \
@ -685,6 +686,36 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
FunctionType::ExtInfo Info = T->getExtInfo();
printFunctionAfter(Info, OS);
if (unsigned quals = T->getTypeQuals()) {
OS << ' ';
AppendTypeQualList(OS, quals, Policy.Restrict);
}
switch (T->getRefQualifier()) {
case RQ_None:
break;
case RQ_LValue:
OS << " &";
break;
case RQ_RValue:
OS << " &&";
break;
}
T->printExceptionSpecification(OS, Policy);
if (T->hasTrailingReturn()) {
OS << " -> ";
print(T->getReturnType(), OS, StringRef());
} else
printAfter(T->getReturnType(), OS);
}
void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info,
raw_ostream &OS) {
if (!InsideCCAttribute) {
switch (Info.getCC()) {
case CC_C:
@ -747,36 +778,13 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
if (Info.getNoReturn())
OS << " __attribute__((noreturn))";
if (Info.getProducesResult())
OS << " __attribute__((ns_returns_retained))";
if (Info.getRegParm())
OS << " __attribute__((regparm ("
<< Info.getRegParm() << ")))";
if (Info.getNoCallerSavedRegs())
OS << "__attribute__((no_caller_saved_registers))";
if (unsigned quals = T->getTypeQuals()) {
OS << ' ';
AppendTypeQualList(OS, quals, Policy.Restrict);
}
switch (T->getRefQualifier()) {
case RQ_None:
break;
case RQ_LValue:
OS << " &";
break;
case RQ_RValue:
OS << " &&";
break;
}
T->printExceptionSpecification(OS, Policy);
if (T->hasTrailingReturn()) {
OS << " -> ";
print(T->getReturnType(), OS, StringRef());
} else
printAfter(T->getReturnType(), OS);
OS << " __attribute__((no_caller_saved_registers))";
}
void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
@ -795,8 +803,7 @@ void TypePrinter::printFunctionNoProtoAfter(const FunctionNoProtoType *T,
SaveAndRestore<bool> NonEmptyPH(HasEmptyPlaceHolder, false);
OS << "()";
if (T->getNoReturnAttr())
OS << " __attribute__((noreturn))";
printFunctionAfter(T->getExtInfo(), OS);
printAfter(T->getReturnType(), OS);
}
@ -1270,6 +1277,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->getAttrKind() == AttributedType::attr_objc_inert_unsafe_unretained)
return;
// Don't print ns_returns_retained unless it had an effect.
if (T->getAttrKind() == AttributedType::attr_ns_returns_retained &&
!T->getEquivalentType()->castAs<FunctionType>()
->getExtInfo().getProducesResult())
return;
// Print nullability type specifiers that occur after
if (T->getAttrKind() == AttributedType::attr_nonnull ||
T->getAttrKind() == AttributedType::attr_nullable ||
@ -1361,6 +1374,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
OS << ')';
break;
case AttributedType::attr_ns_returns_retained:
OS << "ns_returns_retained";
break;
// FIXME: When Sema learns to form this AttributedType, avoid printing the
// attribute again in printFunctionProtoAfter.
case AttributedType::attr_noreturn: OS << "noreturn"; break;

View File

@ -4679,6 +4679,16 @@ void Sema::AddNSConsumedAttr(SourceRange attrRange, Decl *D,
CFConsumedAttr(attrRange, Context, spellingIndex));
}
bool Sema::checkNSReturnsRetainedReturnType(SourceLocation loc,
QualType type) {
if (isValidSubjectOfNSReturnsRetainedAttribute(type))
return false;
Diag(loc, diag::warn_ns_attribute_wrong_return_type)
<< "'ns_returns_retained'" << 0 << 0;
return true;
}
static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
QualType returnType;
@ -4700,6 +4710,8 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
<< Attr.getRange();
return;
}
} else if (Attr.isUsedAsTypeAttr()) {
return;
} else {
AttributeDeclKind ExpectedDeclKind;
switch (Attr.getKind()) {
@ -4743,6 +4755,9 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D,
}
if (!typeOK) {
if (Attr.isUsedAsTypeAttr())
return;
if (isa<ParmVarDecl>(D)) {
S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type)
<< Attr.getName() << /*pointer-to-CF*/2

View File

@ -120,6 +120,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
// Function type attributes.
#define FUNCTION_TYPE_ATTRS_CASELIST \
case AttributeList::AT_NSReturnsRetained: \
case AttributeList::AT_NoReturn: \
case AttributeList::AT_Regparm: \
case AttributeList::AT_AnyX86NoCallerSavedRegisters: \
@ -640,12 +641,6 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType);
break;
case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;
// fallthrough
LLVM_FALLTHROUGH;
FUNCTION_TYPE_ATTRS_CASELIST:
distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType);
break;
@ -2385,6 +2380,11 @@ QualType Sema::BuildFunctionType(QualType T,
[=](unsigned i) { return Loc; });
}
if (EPI.ExtInfo.getProducesResult()) {
// This is just a warning, so we can't fail to build if we see it.
checkNSReturnsRetainedReturnType(Loc, T);
}
if (Invalid)
return QualType();
@ -5017,6 +5017,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
return AttributeList::AT_TypeNullUnspecified;
case AttributedType::attr_objc_kindof:
return AttributeList::AT_ObjCKindOf;
case AttributedType::attr_ns_returns_retained:
return AttributeList::AT_NSReturnsRetained;
}
llvm_unreachable("unexpected attribute kind!");
}
@ -6373,17 +6375,26 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state,
// ns_returns_retained is not always a type attribute, but if we got
// here, we're treating it as one right now.
if (attr.getKind() == AttributeList::AT_NSReturnsRetained) {
assert(S.getLangOpts().ObjCAutoRefCount &&
"ns_returns_retained treated as type attribute in non-ARC");
if (attr.getNumArgs()) return true;
// Delay if this is not a function type.
if (!unwrapped.isFunctionType())
return false;
FunctionType::ExtInfo EI
= unwrapped.get()->getExtInfo().withProducesResult(true);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
// Check whether the return type is reasonable.
if (S.checkNSReturnsRetainedReturnType(attr.getLoc(),
unwrapped.get()->getReturnType()))
return true;
// Only actually change the underlying type in ARC builds.
QualType origType = type;
if (state.getSema().getLangOpts().ObjCAutoRefCount) {
FunctionType::ExtInfo EI
= unwrapped.get()->getExtInfo().withProducesResult(true);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
}
type = S.Context.getAttributedType(AttributedType::attr_ns_returns_retained,
origType, type);
return true;
}
@ -6945,12 +6956,6 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_NSReturnsRetained:
if (!state.getSema().getLangOpts().ObjCAutoRefCount)
break;
// fallthrough into the function attrs
LLVM_FALLTHROUGH;
FUNCTION_TYPE_ATTRS_CASELIST:
attr.setUsedAsTypeAttr();

View File

@ -1447,7 +1447,7 @@ typedef NSString* MyStringTy;
+ (void) consume2:(id) CF_CONSUMED x;
@end
static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}}
static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' only applies to function types; type here is 'int'}}
void test_attr_1(TestOwnershipAttr *X) {
NSString *str = [X returnsAnOwnedString]; // expected-warning{{leak}}

View File

@ -24,7 +24,7 @@ void foo6(){} // expected-note {{previous declaration is here}}
void __attribute__((no_caller_saved_registers)) foo6(); // expected-error {{function declared with 'no_caller_saved_registers' attribute was previously declared without the 'no_caller_saved_registers' attribute}}
int main(int argc, char **argv) {
void (*fp)(int *) = foo; // expected-error {{cannot initialize a variable of type 'void (*)(int *)' with an lvalue of type 'void (int *)__attribute__((no_caller_saved_registers))'}}
void (*fp)(int *) = foo; // expected-error {{cannot initialize a variable of type 'void (*)(int *)' with an lvalue of type 'void (int *) __attribute__((no_caller_saved_registers))'}}
a::foo(&argc);
foo3 func = foo2;
func(&argc);

View File

@ -0,0 +1,18 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fblocks -verify %s
// rdar://20130079
#if __has_feature(objc_arc)
__attribute__((ns_returns_retained)) id (^invalidBlockRedecl)(); // expected-note {{previous definition is here}}
id (^invalidBlockRedecl)(); //expected-error {{redefinition of 'invalidBlockRedecl' with a different type: 'id (^__strong)()' vs 'id ((^__strong))() __attribute__((ns_returns_retained))'}}
#else
__attribute__((ns_returns_retained)) id (^invalidBlockRedecl)();
id (^invalidBlockRedecl)();
#endif
typedef __attribute__((ns_returns_retained)) id (^blockType)();
typedef __attribute__((ns_returns_retained)) int (^invalidBlockType)(); // expected-warning {{'ns_returns_retained' attribute only applies to functions that return an Objective-C object}}
__attribute__((ns_returns_retained)) int functionDecl(); // expected-warning {{'ns_returns_retained' attribute only applies to functions that return an Objective-C object}}