diff options
-rw-r--r-- | clang/include/clang/AST/Decl.h | 10 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 21 | ||||
-rw-r--r-- | clang/test/SemaCXX/anonymous-struct.cpp | 7 |
4 files changed, 42 insertions, 0 deletions
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 439552d1fa1..520e487777d 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -258,6 +258,16 @@ public: /// checking. Should always return true. bool isLinkageValid() const; + /// \brief True if something has required us to compute the linkage + /// of this declaration. + /// + /// Language features which can retroactively change linkage (like a + /// typedef name for linkage purposes) may need to consider this, + /// but hopefully only in transitory ways during parsing. + bool hasLinkageBeenComputed() const { + return hasCachedLinkage(); + } + /// \brief Looks through UsingDecls and ObjCCompatibleAliasDecls for /// the underlying named decl. NamedDecl *getUnderlyingDecl() { diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 406ad8691d2..a32a036d593 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -447,6 +447,10 @@ def ext_no_declarators : ExtWarn<"declaration does not declare anything">, def ext_typedef_without_a_name : ExtWarn<"typedef requires a name">, InGroup<MissingDeclarations>; def err_typedef_not_identifier : Error<"typedef name must be an identifier">; +def err_typedef_changes_linkage : Error<"unsupported: typedef changes linkage" + " of anonymous type, but linkage was already computed">; +def note_typedef_changes_linkage : Note<"use a tag name here to establish " + "linkage prior to definition">; def err_statically_allocated_object : Error< "interface type cannot be statically allocated">; def err_object_cannot_be_passed_returned_by_value : Error< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 59539566e7d..d102cdf55dd 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10144,6 +10144,27 @@ TypedefDecl *Sema::ParseTypedefDecl(Scope *S, Declarator &D, QualType T, if (!Context.hasSameType(T, Context.getTagDeclType(tagFromDeclSpec))) break; + // If we've already computed linkage for the anonymous tag, then + // adding a typedef name for the anonymous decl can change that + // linkage, which might be a serious problem. Diagnose this as + // unsupported and ignore the typedef name. TODO: we should + // pursue this as a language defect and establish a formal rule + // for how to handle it. + if (tagFromDeclSpec->hasLinkageBeenComputed()) { + Diag(D.getIdentifierLoc(), diag::err_typedef_changes_linkage); + + SourceLocation tagLoc = D.getDeclSpec().getTypeSpecTypeLoc(); + tagLoc = Lexer::getLocForEndOfToken(tagLoc, 0, getSourceManager(), + getLangOpts()); + + llvm::SmallString<40> textToInsert; + textToInsert += ' '; + textToInsert += D.getIdentifier()->getName(); + Diag(tagLoc, diag::note_typedef_changes_linkage) + << FixItHint::CreateInsertion(tagLoc, textToInsert); + break; + } + // Otherwise, set this is the anon-decl typedef for the tag. tagFromDeclSpec->setTypedefNameForAnonDecl(NewTD); break; diff --git a/clang/test/SemaCXX/anonymous-struct.cpp b/clang/test/SemaCXX/anonymous-struct.cpp index 8a61041463b..1b5dc13cea0 100644 --- a/clang/test/SemaCXX/anonymous-struct.cpp +++ b/clang/test/SemaCXX/anonymous-struct.cpp @@ -14,3 +14,10 @@ struct E { static struct { }; }; + +template <class T> void foo(T); +typedef struct { // expected-note {{use a tag name here to establish linkage prior to definition}} expected-note {{declared here}} + void test() { + foo(this); // expected-warning {{template argument uses unnamed type}} + } +} A; // expected-error {{unsupported: typedef changes linkage of anonymous type, but linkage was already computed}} |