From: Richard Smith Date: Mon, 28 Jan 2013 22:42:45 +0000 (+0000) Subject: Finish semantic analysis for [[carries_dependency]] attribute. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3a2b7a18a4504f39e3ded0d2b5749c5c80b8b9b5;p=clang 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 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 31a847b129..e092b9a772 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 0d2c948399..fc43ed66fb 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -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. /// diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h index 855485d08e..4957d85e00 100644 --- a/include/clang/Sema/Scope.h +++ b/include/clang/Sema/Scope.h @@ -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 diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 08d6a137f2..e15ab0ae98 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -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(), diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 6acb0b2eb8..a851e2cb85 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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(); } diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 45020fb289..613b0bec5d 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -797,6 +797,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( if (Tok.is(tok::l_paren)) { ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | Scope::DeclScope); SourceLocation DeclEndLoc; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 65bfd1c830..b331defaa3 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1028,8 +1028,8 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, SmallVector KeyIdents; SmallVector KeyLocs; SmallVector ArgInfos; - ParseScope PrototypeScope(this, - Scope::FunctionPrototypeScope|Scope::DeclScope); + ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | Scope::DeclScope); AttributePool allParamAttrs(AttrFactory); while (1) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 20e515d102..1a667daab7 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -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()) { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index fb63953de9..e8d14e6bef 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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() && + !oldDecl->hasAttr()) { + S.Diag(newDecl->getAttr()->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(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(), e = oldDecl->specific_attr_end(); i != e; ++i) { if (!DeclHasAttr(newDecl, *i)) { - InheritableAttr *newAttr = cast((*i)->clone(C)); + InheritableAttr *newAttr = + cast((*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() && + !Old->hasAttr()) { + Diag(New->getAttr()->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); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index bb77adfd45..adeb8e690f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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(D) && !isa(D)) { +static void handleDependencyAttr(Sema &S, Scope *Scope, Decl *D, + const AttributeList &Attr) { + if (isa(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(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; diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp index b15e3c2bb2..9f7ef3ace9 100644 --- a/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p1.cpp @@ -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 diff --git a/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p2.cpp b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p2.cpp new file mode 100644 index 0000000000..d5b0ebf459 --- /dev/null +++ b/test/CXX/dcl.dcl/dcl.attr/dcl.attr.depend/p2.cpp @@ -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();