[cfe-commits] r62014 - in /cfe/trunk: include/clang/Basic/DiagnosticKinds.def include/clang/Parse/Scope.h lib/Parse/ParseCXXInlineMethods.cpp lib/Parse/ParseDecl.cpp lib/Parse/ParseDeclCXX.cpp lib/Parse/Parser.cpp lib/Sema/SemaDecl.cpp test/Sema/type-spec-struct-union.c test/SemaCXX/elaborated-type-specifier.cpp

Douglas Gregor dgregor at apple.com
Fri Jan 9 14:42:13 PST 2009


Author: dgregor
Date: Fri Jan  9 16:42:13 2009
New Revision: 62014

URL: http://llvm.org/viewvc/llvm-project?rev=62014&view=rev
Log:
When we see a reference to a struct, class, or union like "struct X"
that is neither a definition nor a forward declaration and where X has
not yet been declared as a tag, introduce a declaration
into the appropriate scope (which is likely *not* to be the current
scope). The rules for the placement of the declaration differ slightly
in C and C++, so we implement both and test the various corner
cases. This implementation isn't 100% correct due to some lingering
issues with the function prototype scope (for a function parameter
list) not being the same scope as the scope of the function
definition. Testcase is FIXME'd; this probably isn't an important issue.

Addresses <rdar://problem/6484805>.

Added:
    cfe/trunk/test/Sema/type-spec-struct-union.c
    cfe/trunk/test/SemaCXX/elaborated-type-specifier.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticKinds.def
    cfe/trunk/include/clang/Parse/Scope.h
    cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/Parser.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticKinds.def?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Fri Jan  9 16:42:13 2009
@@ -726,6 +726,8 @@
      "'%0' cannot be the name of a variable or data member")
 DIAG(err_parameter_name_omitted, ERROR,
      "parameter name omitted")
+DIAG(warn_decl_in_param_list, WARNING,
+     "declaration of %0 will not be visible outside of this function")
 
 DIAG(err_declarator_need_ident, ERROR,
      "declarator requires an identifier")

Modified: cfe/trunk/include/clang/Parse/Scope.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Scope.h?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/include/clang/Parse/Scope.h (original)
+++ cfe/trunk/include/clang/Parse/Scope.h Fri Jan  9 16:42:13 2009
@@ -47,8 +47,8 @@
     /// ControlScope - The controlling scope in a if/switch/while/for statement.
     ControlScope = 0x10,
 
-    /// CXXClassScope - The scope of a C++ struct/union/class definition.
-    CXXClassScope = 0x20,
+    /// ClassScope - The scope of a struct/union/class definition.
+    ClassScope = 0x20,
     
     /// BlockScope - This is a scope that corresponds to a block object.
     /// Blocks serve as top-level scopes for some objects like labels, they
@@ -60,7 +60,11 @@
     /// template parameters of a C++ template. Template parameter
     /// scope starts at the 'template' keyword and ends when the
     /// template declaration ends.
-    TemplateParamScope = 0x80
+    TemplateParamScope = 0x80,
+
+    /// FunctionPrototypeScope - This is a scope that corresponds to the
+    /// parameters within a function prototype.
+    FunctionPrototypeScope = 0x100
   };
 private:
   /// The parent scope for this scope.  This is null for the translation-unit
@@ -73,7 +77,7 @@
   
   /// Flags - This contains a set of ScopeFlags, which indicates how the scope
   /// interrelates with other control flow statements.
-  unsigned Flags : 8;
+  unsigned Flags : 9;
   
   /// WithinElse - Whether this scope is part of the "else" branch in
   /// its parent ControlScope.
@@ -197,9 +201,9 @@
   void* getEntity() const { return Entity; }
   void setEntity(void *E) { Entity = E; }
 
-  /// isCXXClassScope - Return true if this scope is a C++ class scope.
-  bool isCXXClassScope() const {
-    return (getFlags() & Scope::CXXClassScope);
+  /// isClassScope - Return true if this scope is a class/struct/union scope.
+  bool isClassScope() const {
+    return (getFlags() & Scope::ClassScope);
   }
 
   /// isInCXXInlineMethodScope - Return true if this scope is a C++ inline
@@ -207,7 +211,7 @@
   bool isInCXXInlineMethodScope() const {
     if (const Scope *FnS = getFnParent()) {
       assert(FnS->getParent() && "TUScope not created?");
-      return FnS->getParent()->isCXXClassScope();
+      return FnS->getParent()->isClassScope();
     }
     return false;
   }
@@ -218,6 +222,12 @@
     return getFlags() & Scope::TemplateParamScope;
   }
 
+  /// isFunctionPrototypeScope - Return true if this scope is a
+  /// function prototype scope.
+  bool isFunctionPrototypeScope() const {
+    return getFlags() & Scope::FunctionPrototypeScope;
+  }
+
   /// isWithinElse - Whether we are within the "else" of the
   /// ControlParent (if any).
   bool isWithinElse() const { return WithinElse; }

Modified: cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp (original)
+++ cfe/trunk/lib/Parse/ParseCXXInlineMethods.cpp Fri Jan  9 16:42:13 2009
@@ -75,7 +75,8 @@
 
     // Introduce the parameters into scope and parse their default
     // arguments.
-    ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+    ParseScope PrototypeScope(this, 
+                              Scope::FunctionPrototypeScope|Scope::DeclScope);
     for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) {
       // Introduce the parameter into scope.
       Actions.ActOnDelayedCXXMethodParameter(CurScope, LM.DefaultArgs[I].Param);

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Fri Jan  9 16:42:13 2009
@@ -526,7 +526,7 @@
       // constructor declaration. We're done with the decl-specifiers
       // and will treat this token as an identifier.
       if (getLang().CPlusPlus && 
-          CurScope->isCXXClassScope() &&
+          CurScope->isClassScope() &&
           Actions.isCurrentClassName(*Tok.getIdentifierInfo(), CurScope) && 
           NextToken().getKind() == tok::l_paren)
         goto DoneWithDeclSpec;
@@ -948,7 +948,7 @@
                                   unsigned TagType, DeclTy *TagDecl) {
   SourceLocation LBraceLoc = ConsumeBrace();
   
-  ParseScope StructScope(this, Scope::DeclScope);
+  ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
   Actions.ActOnTagStartDefinition(CurScope, TagDecl);
 
   // Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
@@ -1875,7 +1875,7 @@
 
   // Enter function-declaration scope, limiting any declarators to the
   // function prototype scope, including parameter declarators.
-  ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+  ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope);
   
   bool IsVariadic = false;
   while (1) {

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Fri Jan  9 16:42:13 2009
@@ -720,7 +720,7 @@
 
   SourceLocation LBraceLoc = ConsumeBrace();
 
-  if (!CurScope->isCXXClassScope() && // Not about to define a nested class.
+  if (!CurScope->isClassScope() && // Not about to define a nested class.
       CurScope->isInCXXInlineMethodScope()) {
     // We will define a local class of an inline method.
     // Push a new LexedMethodsForTopClass for its inline methods.
@@ -728,7 +728,7 @@
   }
 
   // Enter a scope for the class.
-  ParseScope ClassScope(this, Scope::CXXClassScope|Scope::DeclScope);
+  ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope);
 
   Actions.ActOnTagStartDefinition(CurScope, TagDecl);
 
@@ -782,7 +782,7 @@
   //
   // FIXME: Only function bodies and constructor ctor-initializers are
   // parsed correctly, fix the rest.
-  if (!CurScope->getParent()->isCXXClassScope()) {
+  if (!CurScope->getParent()->isClassScope()) {
     // We are not inside a nested class. This class and its nested classes
     // are complete and we can parse the delayed portions of method
     // declarations and the lexed inline method definitions.

Modified: cfe/trunk/lib/Parse/Parser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/lib/Parse/Parser.cpp (original)
+++ cfe/trunk/lib/Parse/Parser.cpp Fri Jan  9 16:42:13 2009
@@ -570,7 +570,7 @@
 
   // Enter function-declaration scope, limiting any declarators to the
   // function prototype scope, including parameter declarators.
-  ParseScope PrototypeScope(this, Scope::FnScope|Scope::DeclScope);
+  ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope);
 
   // Read all the argument declarations.
   while (isDeclarationSpecifier()) {

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=62014&r1=62013&r2=62014&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jan  9 16:42:13 2009
@@ -2859,6 +2859,7 @@
   }
   
   DeclContext *DC = CurContext;
+  DeclContext *LexicalContext = CurContext;
   ScopedDecl *PrevDecl = 0;
 
   if (Name && SS.isNotEmpty()) {
@@ -2971,6 +2972,35 @@
         PrevDecl = 0;
       }
     }
+  } else if (TK == TK_Reference && SS.isEmpty() && Name &&
+             (Kind != TagDecl::TK_enum))  {
+    // C++ [basic.scope.pdecl]p5:
+    //   -- for an elaborated-type-specifier of the form 
+    //
+    //          class-key identifier
+    //
+    //      if the elaborated-type-specifier is used in the
+    //      decl-specifier-seq or parameter-declaration-clause of a
+    //      function defined in namespace scope, the identifier is
+    //      declared as a class-name in the namespace that contains
+    //      the declaration; otherwise, except as a friend
+    //      declaration, the identifier is declared in the smallest
+    //      non-class, non-function-prototype scope that contains the
+    //      declaration.
+    //
+    // C99 6.7.2.3p8 has a similar (but not identical!) provision for
+    // C structs and unions.
+
+    // Find the context where we'll be declaring the tag.
+    while (DC->isRecord())
+      DC = DC->getParent();
+    LexicalContext = DC;
+
+    // Find the scope where we'll be declaring the tag.
+    while (S->isClassScope() || 
+           (getLangOptions().CPlusPlus && S->isFunctionPrototypeScope()) ||
+           (S->getFlags() & Scope::DeclScope == 0))
+      S = S->getParent();
   }
 
 CreateNewDecl:
@@ -3025,9 +3055,13 @@
   if (Attr)
     ProcessDeclAttributeList(New, Attr);
 
+  // If we're declaring or defining 
+  if (Name && S->isFunctionPrototypeScope() && !getLangOptions().CPlusPlus)
+    Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New);
+
   // Set the lexical context. If the tag has a C++ scope specifier, the
   // lexical context will be different from the semantic context.
-  New->setLexicalDeclContext(CurContext);
+  New->setLexicalDeclContext(LexicalContext);
   
   // If this has an identifier, add it to the scope stack.
   if (Name) {
@@ -3037,13 +3071,20 @@
       S = S->getParent();
     
     // Add it to the decl chain.
-    PushOnScopeChains(New, S);
+    if (LexicalContext != CurContext) {
+      // FIXME: PushOnScopeChains should not rely on CurContext!
+      DeclContext *OldContext = CurContext;
+      CurContext = LexicalContext;
+      PushOnScopeChains(New, S);
+      CurContext = OldContext;
+    } else 
+      PushOnScopeChains(New, S);
   } else if (getLangOptions().CPlusPlus) {
     // FIXME: We also want to do this for C, but if this tag is
     // defined within a structure CurContext will point to the context
     // enclosing the structure, and we would end up inserting the tag
     // type into the wrong place.
-    CurContext->addDecl(Context, New);
+    LexicalContext->addDecl(Context, New);
   }
 
   return New;

Added: cfe/trunk/test/Sema/type-spec-struct-union.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/type-spec-struct-union.c?rev=62014&view=auto

==============================================================================
--- cfe/trunk/test/Sema/type-spec-struct-union.c (added)
+++ cfe/trunk/test/Sema/type-spec-struct-union.c Fri Jan  9 16:42:13 2009
@@ -0,0 +1,37 @@
+// RUN: clang -fsyntax-only -pedantic -verify %s
+
+/* This test checks the introduction of struct and union types based
+   on a type specifier of the form "struct-or-union identifier" when they
+   type has not yet been declared. See C99 6.7.2.3p8. */
+
+typedef struct S1 {
+  union {
+    struct S2 *x;
+    struct S3 *y;
+  } u1;
+} S1;
+
+int test_struct_scope(S1 *s1, struct S2 *s2, struct S3 *s3) {
+  if (s1->u1.x == s2) return 1;
+  if (s1->u1.y == s3) return 1;
+  return 0;
+}
+
+int test_struct_scope_2(S1 *s1) {
+  struct S2 { int x; } *s2 = 0;
+  if (s1->u1.x == s2) return 1; /* expected-warning {{comparison of distinct pointer types ('struct S2 *' and 'struct S2 *')}} */
+  return 0;
+}
+
+// FIXME: We do not properly implement C99 6.2.1p4, which says that
+// the type "struct S4" declared in the function parameter list has
+// block scope within the function definition. The problem, in this
+// case, is that the code is ill-formed but we warn about the two S4's
+// being incompatible (we think they are two different types).
+int test_struct_scope_3(struct S4 * s4) { // expected-warning{{declaration of 'struct S4' will not be visible outside of this function}}
+  struct S4 { int y; } *s4_2 = 0;
+  /*  if (s4 == s4_2) return 1; */
+  return 0;
+}
+
+void f(struct S5 { int y; } s5); // expected-warning{{declaration of 'struct S5' will not be visible outside of this function}}

Added: cfe/trunk/test/SemaCXX/elaborated-type-specifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/elaborated-type-specifier.cpp?rev=62014&view=auto

==============================================================================
--- cfe/trunk/test/SemaCXX/elaborated-type-specifier.cpp (added)
+++ cfe/trunk/test/SemaCXX/elaborated-type-specifier.cpp Fri Jan  9 16:42:13 2009
@@ -0,0 +1,48 @@
+// RUN: clang -fsyntax-only -verify %s
+
+// Test the use of elaborated-type-specifiers to inject the names of
+// structs (or classes or unions) into an outer scope as described in
+// C++ [basic.scope.pdecl]p5.
+typedef struct S1 {
+  union {
+    struct S2 *x;
+    struct S3 *y;
+  };
+} S1;
+
+bool test_elab(S1 *s1, struct S2 *s2, struct S3 *s3) {
+  if (s1->x == s2) return true;
+  if (s1->y == s3) return true;
+  return false;
+}
+
+namespace NS {
+  class X {
+  public:
+    void test_elab2(struct S4 *s4);
+  };
+
+  void X::test_elab2(S4 *s4) { }
+}
+
+void test_X_elab(NS::X x) {
+  struct S4 *s4 = 0;
+  x.test_elab2(s4); // expected-error{{incompatible type passing 'struct S4 *', expected 'struct S4 *'}}
+}
+
+namespace NS {
+  S4 *get_S4();
+}
+
+void test_S5_scope() {
+  S4 *s4; // expected-error{{use of undeclared identifier 'S4'}}
+}
+
+// FIXME: the warning below should be an error!
+int test_funcparam_scope(struct S5 * s5) {
+  struct S5 { int y; } *s5_2 = 0;
+  if (s5 == s5_2) return 1; // expected-warning {{comparison of distinct pointer types ('struct S5 *' and 'struct S5 *')}}
+  return 0;
+}
+
+





More information about the cfe-commits mailing list