Finish semantic analysis for [[carries_dependency]] attribute.

This required plumbing through a new flag to determine whether a ParmVarDecl is
actually a parameter of a function declaration (as opposed to a function
typedef etc, where the attribute is prohibited). Weirdly, this attribute (just
like [[noreturn]]) cannot be applied to a function type, just to a function
declaration (and its parameters).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@173726 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2013-01-28 22:42:45 +00:00
Родитель 319d8fc222
Коммит 3a2b7a18a4
12 изменённых файлов: 171 добавлений и 37 удалений

Просмотреть файл

@ -5898,6 +5898,14 @@ def err_noreturn_missing_on_first_decl : Error<
"function declared '[[noreturn]]' after its first declaration">;
def note_noreturn_missing_first_decl : Note<
"declaration missing '[[noreturn]]' attribute is here">;
def err_carries_dependency_missing_on_first_decl : Error<
"%select{function|parameter}0 declared '[[carries_dependency]]' "
"after its first declaration">;
def note_carries_dependency_missing_first_decl : Note<
"declaration missing '[[carries_dependency]]' attribute is here">;
def err_carries_dependency_param_not_function_decl : Error<
"'[[carries_dependency]]' attribute only allowed on parameter in a function "
"declaration or lambda">;
def err_block_on_nonlocal : Error<
"__block attribute not allowed, only allowed on local variables">;
def err_block_on_vm : Error<

Просмотреть файл

@ -1879,6 +1879,44 @@ public:
/// type. This routine checks for both cases.
bool isDeclarationOfFunction() const;
/// \brief Return true if a function declarator at this position would be a
/// function declaration.
bool isFunctionDeclaratorAFunctionDeclaration() const {
if (getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef)
return false;
for (unsigned I = 0, N = getNumTypeObjects(); I != N; ++I)
if (getTypeObject(I).Kind != DeclaratorChunk::Paren)
return false;
switch (Context) {
case FileContext:
case MemberContext:
case BlockContext:
return true;
case ForContext:
case ConditionContext:
case KNRTypeListContext:
case TypeNameContext:
case AliasDeclContext:
case AliasTemplateContext:
case PrototypeContext:
case ObjCParameterContext:
case ObjCResultContext:
case TemplateParamContext:
case CXXNewContext:
case CXXCatchContext:
case ObjCCatchContext:
case BlockLiteralContext:
case LambdaExprContext:
case TemplateTypeArgContext:
case TrailingReturnContext:
return false;
}
llvm_unreachable("unknown context kind!");
}
/// takeAttributes - Takes attributes from the given parsed-attributes
/// set and add them to this declarator.
///

Просмотреть файл

@ -32,61 +32,66 @@ public:
/// ScopeFlags - These are bitfields that are or'd together when creating a
/// scope, which defines the sorts of things the scope contains.
enum ScopeFlags {
/// FnScope - This indicates that the scope corresponds to a function, which
/// \brief This indicates that the scope corresponds to a function, which
/// means that labels are set here.
FnScope = 0x01,
/// BreakScope - This is a while,do,switch,for, etc that can have break
/// stmts embedded into it.
/// \brief This is a while, do, switch, for, etc that can have break
/// statements embedded into it.
BreakScope = 0x02,
/// ContinueScope - This is a while,do,for, which can have continue
/// stmt embedded into it.
/// \brief This is a while, do, for, which can have continue statements
/// embedded into it.
ContinueScope = 0x04,
/// DeclScope - This is a scope that can contain a declaration. Some scopes
/// \brief This is a scope that can contain a declaration. Some scopes
/// just contain loop constructs but don't contain decls.
DeclScope = 0x08,
/// ControlScope - The controlling scope in a if/switch/while/for statement.
/// \brief The controlling scope in a if/switch/while/for statement.
ControlScope = 0x10,
/// ClassScope - The scope of a struct/union/class definition.
/// \brief The scope of a struct/union/class definition.
ClassScope = 0x20,
/// BlockScope - This is a scope that corresponds to a block/closure object.
/// \brief This is a scope that corresponds to a block/closure object.
/// Blocks serve as top-level scopes for some objects like labels, they
/// also prevent things like break and continue. BlockScopes always have
/// the FnScope and DeclScope flags set as well.
BlockScope = 0x40,
/// TemplateParamScope - This is a scope that corresponds to the
/// \brief This is a scope that corresponds to the
/// template parameters of a C++ template. Template parameter
/// scope starts at the 'template' keyword and ends when the
/// template declaration ends.
TemplateParamScope = 0x80,
/// FunctionPrototypeScope - This is a scope that corresponds to the
/// \brief This is a scope that corresponds to the
/// parameters within a function prototype.
FunctionPrototypeScope = 0x100,
/// AtCatchScope - This is a scope that corresponds to the Objective-C
/// \brief This is a scope that corresponds to the parameters within
/// a function prototype for a function declaration (as opposed to any
/// other kind of function declarator). Always has FunctionPrototypeScope
/// set as well.
FunctionDeclarationScope = 0x200,
/// \brief This is a scope that corresponds to the Objective-C
/// \@catch statement.
AtCatchScope = 0x200,
AtCatchScope = 0x400,
/// ObjCMethodScope - This scope corresponds to an Objective-C method body.
/// \brief This scope corresponds to an Objective-C method body.
/// It always has FnScope and DeclScope set as well.
ObjCMethodScope = 0x400,
ObjCMethodScope = 0x800,
/// SwitchScope - This is a scope that corresponds to a switch statement.
SwitchScope = 0x800,
/// \brief This is a scope that corresponds to a switch statement.
SwitchScope = 0x1000,
/// TryScope - This is the scope of a C++ try statement.
TryScope = 0x1000,
/// \brief This is the scope of a C++ try statement.
TryScope = 0x2000,
/// FnTryCatchScope - This is the scope for a function-level C++ try or
/// catch scope.
FnTryCatchScope = 0x2000
/// \brief This is the scope for a function-level C++ try or catch scope.
FnTryCatchScope = 0x4000
};
private:
/// The parent scope for this scope. This is null for the translation-unit

Просмотреть файл

@ -293,8 +293,8 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
// Introduce the parameters into scope and parse their default
// arguments.
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope|Scope::DeclScope);
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope | Scope::DeclScope);
for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
// Introduce the parameter into scope.
Actions.ActOnDelayedCXXMethodParameter(getCurScope(),

Просмотреть файл

@ -4522,7 +4522,10 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope|Scope::DeclScope);
Scope::FunctionPrototypeScope|Scope::DeclScope|
(D.isFunctionDeclaratorAFunctionDeclaration()
? Scope::FunctionDeclarationScope : 0));
// The paren may be part of a C++ direct initializer, eg. "int x(1);".
// In such a case, check if we actually have a function declarator; if it
// is not, the declarator has been fully parsed.
@ -4652,7 +4655,9 @@ void Parser::ParseParenDeclarator(Declarator &D) {
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope|Scope::DeclScope);
Scope::FunctionPrototypeScope | Scope::DeclScope |
(D.isFunctionDeclaratorAFunctionDeclaration()
? Scope::FunctionDeclarationScope : 0));
ParseFunctionDeclarator(D, attrs, T, false, RequiresArg);
PrototypeScope.Exit();
}

Просмотреть файл

@ -797,6 +797,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
if (Tok.is(tok::l_paren)) {
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope |
Scope::DeclScope);
SourceLocation DeclEndLoc;

Просмотреть файл

@ -1028,8 +1028,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
SmallVector<IdentifierInfo *, 12> KeyIdents;
SmallVector<SourceLocation, 12> KeyLocs;
SmallVector<Sema::ObjCArgInfo, 12> ArgInfos;
ParseScope PrototypeScope(this,
Scope::FunctionPrototypeScope|Scope::DeclScope);
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope | Scope::DeclScope);
AttributePool allParamAttrs(AttrFactory);
while (1) {

Просмотреть файл

@ -1097,7 +1097,8 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) {
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope);
ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope | Scope::DeclScope);
// Read all the argument declarations.
while (isDeclarationSpecifier()) {

Просмотреть файл

@ -1911,6 +1911,9 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
/// mergeDeclAttributes - Copy attributes from the Old decl to the New one.
void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
AvailabilityMergeKind AMK) {
if (!Old->hasAttrs() && !New->hasAttrs())
return;
// attributes declared post-definition are currently ignored
checkNewAttributesAfterDef(*this, New, Old);
@ -1956,7 +1959,25 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
/// to the new one.
static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
const ParmVarDecl *oldDecl,
ASTContext &C) {
Sema &S) {
// C++11 [dcl.attr.depend]p2:
// The first declaration of a function shall specify the
// carries_dependency attribute for its declarator-id if any declaration
// of the function specifies the carries_dependency attribute.
if (newDecl->hasAttr<CarriesDependencyAttr>() &&
!oldDecl->hasAttr<CarriesDependencyAttr>()) {
S.Diag(newDecl->getAttr<CarriesDependencyAttr>()->getLocation(),
diag::err_carries_dependency_missing_on_first_decl) << 1/*Param*/;
// Find the first declaration of the parameter.
// FIXME: Should we build redeclaration chains for function parameters?
const FunctionDecl *FirstFD =
cast<FunctionDecl>(oldDecl->getDeclContext())->getFirstDeclaration();
const ParmVarDecl *FirstVD =
FirstFD->getParamDecl(oldDecl->getFunctionScopeIndex());
S.Diag(FirstVD->getLocation(),
diag::note_carries_dependency_missing_first_decl) << 1/*Param*/;
}
if (!oldDecl->hasAttrs())
return;
@ -1970,7 +1991,8 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
i = oldDecl->specific_attr_begin<InheritableParamAttr>(),
e = oldDecl->specific_attr_end<InheritableParamAttr>(); i != e; ++i) {
if (!DeclHasAttr(newDecl, *i)) {
InheritableAttr *newAttr = cast<InheritableParamAttr>((*i)->clone(C));
InheritableAttr *newAttr =
cast<InheritableParamAttr>((*i)->clone(S.Context));
newAttr->setInherited(true);
newDecl->addAttr(newAttr);
foundAny = true;
@ -2295,6 +2317,18 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) {
diag::note_noreturn_missing_first_decl);
}
// C++11 [dcl.attr.depend]p2:
// The first declaration of a function shall specify the
// carries_dependency attribute for its declarator-id if any declaration
// of the function specifies the carries_dependency attribute.
if (New->hasAttr<CarriesDependencyAttr>() &&
!Old->hasAttr<CarriesDependencyAttr>()) {
Diag(New->getAttr<CarriesDependencyAttr>()->getLocation(),
diag::err_carries_dependency_missing_on_first_decl) << 0/*Function*/;
Diag(Old->getFirstDeclaration()->getLocation(),
diag::note_carries_dependency_missing_first_decl) << 0/*Function*/;
}
// (C++98 8.3.5p3):
// All declarations for a function shall agree exactly in both the
// return type and the parameter-type-list.
@ -2487,7 +2521,7 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
if (New->getNumParams() == Old->getNumParams())
for (unsigned i = 0, e = New->getNumParams(); i != e; ++i)
mergeParamDeclAttributes(New->getParamDecl(i), Old->getParamDecl(i),
Context);
*this);
if (getLangOpts().CPlusPlus)
return MergeCXXFunctionDecl(New, Old, S);
@ -2514,7 +2548,7 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
for (ObjCMethodDecl::param_iterator
ni = newMethod->param_begin(), ne = newMethod->param_end();
ni != ne && oi != oe; ++ni, ++oi)
mergeParamDeclAttributes(*ni, *oi, Context);
mergeParamDeclAttributes(*ni, *oi, *this);
CheckObjCMethodOverride(newMethod, oldMethod);
}

Просмотреть файл

@ -24,6 +24,7 @@
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "llvm/ADT/StringExtras.h"
using namespace clang;
using namespace sema;
@ -1815,13 +1816,25 @@ static void handleVecReturnAttr(Sema &S, Decl *D, const AttributeList &Attr) {
Attr.getAttributeSpellingListIndex()));
}
static void handleDependencyAttr(Sema &S, Decl *D, const AttributeList &Attr) {
if (!isa<FunctionDecl>(D) && !isa<ParmVarDecl>(D)) {
static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D,
const AttributeList &Attr) {
if (isa<ParmVarDecl>(D)) {
// [[carries_dependency]] can only be applied to a parameter if it is a
// parameter of a function declaration or lambda.
if (!(Scope->getFlags() & clang::Scope::FunctionDeclarationScope)) {
S.Diag(Attr.getLoc(),
diag::err_carries_dependency_param_not_function_decl);
return;
}
} else if (!isa<FunctionDecl>(D)) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
<< Attr.getName() << ExpectedFunctionMethodOrParameter;
return;
}
// FIXME: Actually store the attribute on the declaration
D->addAttr(::new (S.Context) CarriesDependencyAttr(
Attr.getRange(), S.Context,
Attr.getAttributeSpellingListIndex()));
}
static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
@ -4494,7 +4507,8 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_Annotate: handleAnnotateAttr (S, D, Attr); break;
case AttributeList::AT_Availability:handleAvailabilityAttr(S, D, Attr); break;
case AttributeList::AT_CarriesDependency:
handleDependencyAttr (S, D, Attr); break;
handleDependencyAttr(S, scope, D, Attr);
break;
case AttributeList::AT_Common: handleCommonAttr (S, D, Attr); break;
case AttributeList::AT_CUDAConstant:handleConstantAttr (S, D, Attr); break;
case AttributeList::AT_Constructor: handleConstructorAttr (S, D, Attr); break;

Просмотреть файл

@ -10,6 +10,20 @@ int f3(int param [[carries_dependency]]); // ok
[[carries_dependency]] int (*f4)(); // expected-error {{'carries_dependency' attribute only applies to functions, methods, and parameters}}
int (*f5 [[carries_dependency]])(); // expected-error {{'carries_dependency' attribute only applies to functions, methods, and parameters}}
int (*f6)() [[carries_dependency]]; // expected-error {{'carries_dependency' attribute cannot be applied to types}}
int (*f7)(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
int (((f8)))(int n [[carries_dependency]]); // ok
int (*f9(int n))(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
int typedef f10(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
using T = int(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
struct S {
[[carries_dependency]] int f(int n [[carries_dependency]]); // ok
int (*p)(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
};
void f() {
[[carries_dependency]] int f(int n [[carries_dependency]]); // ok
[[carries_dependency]] // expected-error {{'carries_dependency' attribute only applies to functions, methods, and parameters}}
int (*p)(int n [[carries_dependency]]); // expected-error {{'[[carries_dependency]]' attribute only allowed on parameter in a function declaration}}
}
auto l1 = [](int n [[carries_dependency]]) {};
// There's no way to write a lambda such that the return value carries

Просмотреть файл

@ -0,0 +1,14 @@
// RUN: %clang_cc1 -verify -std=c++11 %s
int f(int); // expected-note 2{{declaration missing '[[carries_dependency]]' attribute is here}}
[[carries_dependency]] int f(int); // expected-error {{function declared '[[carries_dependency]]' after its first declaration}}
int f(int n [[carries_dependency]]); // expected-error {{parameter declared '[[carries_dependency]]' after its first declaration}}
int g([[carries_dependency]] int n); // expected-note {{declaration missing '[[carries_dependency]]' attribute is here}}
int g(int);
[[carries_dependency]] int g(int); // expected-error {{function declared '[[carries_dependency]]' after its first declaration}}
int g(int n [[carries_dependency]]);
int h [[carries_dependency]]();
int h();
[[carries_dependency]] int h();