[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-dev
Subject:    Re: [cfe-dev] [PATCH] Allow unnamed structure or union fields
From:       "Pierre d'Herbemont" <pdherbemont () free ! fr>
Date:       2008-11-15 22:18:45
Message-ID: 15922A60-194A-4076-BADF-E660967D90F1 () free ! fr
[Download RAW message or body]

Hi Doug,

On Nov 10, 2008, at 4:32 PM, Doug Gregor wrote:

> On Sun, Nov 9, 2008 at 11:53 AM, Pierre d'Herbemont <pdherbemont@free.fr 
> > wrote:
>> This patch enables unnamed structure or union fields withing  
>> structs/unions
>> when -std=gnu99 is used. Cf [1].
>>
>> Note, as stated in [1], struct { int a; union { int a; } } is  
>> discouraged,
>> but won't produce any error nor warning.
>
> As Eli said, this patch should really produce an error

Latest patch has something that checks for error now.

>> It is unclear for me that we really want to add anonymous structure  
>> support
>> in RecordDecl::getMember, as implementation could vary given the  
>> language
>> standard. Yet, not implementing here could lead to easy and  
>> unoticeable
>> mistakes if the caller doesn't implement anonymous structure  
>> resolving.
>
> Yeah, this is an interesting issue... what happens if you try to
> actually generate code for your example program? And execute the
> function dummy()? Since we have code-generation support for most of C,
> I'd like for implementation of new C features to support
> code-generation from the start. Maybe it will just work, but I'm
> guessing that if it *doesn't* work, we may have to consider extending
> the AST more to make it work.

Well, I didn't figure that out at first. Latest patch fixes that. This  
time it doesn't changes the AST, instead it adds fakely create the  
missing MemberExpr to navigate in the anonymous fields.

An other solution involves extending the MemberExpr, but this requires  
more changes on clients (like CodeGen). What do you prefer?

--
After writing this mail I saw Patrick Walton patch... :)

I guess we could merge the two patches, as Patrick's modification to  
the AST seems to be the preferred solution. I am still attaching the  
patch before merge.
--

> The option name "AllowUnnamedStructOrUnionInStructOrUnion" is really,
> really long :)

I like long explicit name :) But it's now gone...

> I'd be inclined to remove this option. Instead, just allow this
> extension unless NoExtensions is set.

Ok, this is what is done in latest patch. Yet with -std=c99,  
NoExtensions doesn't seem to be set.

Though, we'll get warning with -pedantic.

> On a related note, anonymous
> unions (but anonymous structs aren't) are legal in C++, so the patch
> should reflect that.

It should now :)

In this patch:
- The Expr tree is expanded for the unnamed struct/union/class (An  
other solution is Patrick Walton's patch :) )
- We produce errors on field redefinitions such as "struct foo { int  
a; union { float a; int b} };"
- "struct a { struct foo; struct bar {}; }" is correctly filtered
- "struct { int a; }" outside of a struct/union/class is correctly  
filtered.
- Specific hack for the obj-c case property that should not allow  
anonymous structs when generating objc properties.
- llvm code generation seems correct.
- All clang tests pass

Things I am not proud of:
- getStructOrUnionIdentifierLoc(), the name is probably badly chosen,  
but that's how I manage to filter out "struct a { struct foo; }"

I'll be happy to ear your feedbacks on that one as well.

Pierre.


["0001-Allow-unnamed-structure-or-union-fields-within-struc.patch" (0001-Allow-unnamed-structure-or-union-fields-within-struc.patch)]

From aad4e0780e773ea4b28b11131a5b4c8ae7767d78 Mon Sep 17 00:00:00 2001
From: Pierre d'Herbemont <pdherbemont@videolan.org>
Date: Wed, 12 Nov 2008 21:22:47 +0100
Subject: [PATCH] Allow unnamed structure or union fields within structs/unions.

---
 include/clang/Basic/DiagnosticKinds.def |    2 +
 include/clang/Parse/DeclSpec.h          |    7 ++
 include/clang/Parse/Parser.h            |    3 +-
 lib/Parse/DeclSpec.cpp                  |   10 +++
 lib/Parse/ParseDecl.cpp                 |   39 +++++++++++--
 lib/Parse/ParseDeclCXX.cpp              |    2 +
 lib/Parse/ParseObjc.cpp                 |    6 +-
 lib/Sema/Sema.h                         |   10 +++
 lib/Sema/SemaDecl.cpp                   |   97 ++++++++++++++++++++++++++++++-
 lib/Sema/SemaExpr.cpp                   |   62 ++++++++++++++++----
 test/Sema/unnamed-struct-in-struct-2.c  |   55 +++++++++++++++++
 test/Sema/unnamed-struct-in-struct.c    |   26 ++++++++
 12 files changed, 297 insertions(+), 22 deletions(-)
 create mode 100644 test/Sema/unnamed-struct-in-struct-2.c
 create mode 100644 test/Sema/unnamed-struct-in-struct.c

diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 9073e26..8282c65 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -249,6 +249,8 @@ DIAG(err_pp_I_dash_not_supported, ERROR,
 
 DIAG(w_no_declarators, WARNING,
      "declaration does not declare anything")
+DIAG(ext_unnamed_struct_union_class, EXTENSION,
+     "Unnamed struct/union/class usage is a GNU extension")
 DIAG(w_asm_qualifier_ignored, WARNING,
      "ignored %0 qualifier on asm")
 
diff --git a/include/clang/Parse/DeclSpec.h b/include/clang/Parse/DeclSpec.h
index 94bd839..7f3871d 100644
--- a/include/clang/Parse/DeclSpec.h
+++ b/include/clang/Parse/DeclSpec.h
@@ -140,6 +140,7 @@ private:
   SourceLocation TSWLoc, TSCLoc, TSSLoc, TSTLoc;
   SourceLocation TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc;
   SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc;
+  SourceLocation SU_identifierLoc;
   
   bool BadSpecifier(TST T, const char *&PrevSpec);
   bool BadSpecifier(TQ T, const char *&PrevSpec);
@@ -198,6 +199,10 @@ public:
   SourceLocation getTypeSpecComplexLoc() const { return TSCLoc; }
   SourceLocation getTypeSpecSignLoc() const { return TSSLoc; }
   SourceLocation getTypeSpecTypeLoc() const { return TSTLoc; }
+
+  /// getStructOrUnionIdentifierLoc - return the location of "foo"
+  /// in Decl like "struct foo {...};" or "union foo {...};"
+  SourceLocation getStructOrUnionIdentifierLoc() const { return SU_identifierLoc; }
   
   /// getSpecifierName - Turn a type-specifier-type into a string like "_Bool"
   /// or "union".
@@ -258,6 +263,8 @@ public:
   bool SetTypeSpecSign(TSS S, SourceLocation Loc, const char *&PrevSpec);
   bool SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec,
                        Action::TypeTy *TypeRep = 0);
+
+  bool SetStructOrUnionIdentifierLoc(SourceLocation Loc);
   
   bool SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
                    const LangOptions &Lang);
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 9eefe55..72d8f90 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -635,7 +635,8 @@ private:
   void ParseStructUnionBody(SourceLocation StartLoc, unsigned TagType,
                             DeclTy *TagDecl);
   void ParseStructDeclaration(DeclSpec &DS,
-                              llvm::SmallVectorImpl<FieldDeclarator> &Fields);
+                              llvm::SmallVectorImpl<FieldDeclarator> &Fields,
+                              bool AllowUnnamed);
                               
   bool isDeclarationSpecifier();
   bool isTypeSpecifierQualifier();
diff --git a/lib/Parse/DeclSpec.cpp b/lib/Parse/DeclSpec.cpp
index d61d6c5..5a96d4f 100644
--- a/lib/Parse/DeclSpec.cpp
+++ b/lib/Parse/DeclSpec.cpp
@@ -183,6 +183,16 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
   return false;
 }
 
+bool DeclSpec::SetStructOrUnionIdentifierLoc(SourceLocation Loc)
+{
+  assert(TypeSpecType == TST_struct ||
+         TypeSpecType == TST_union ||
+         TypeSpecType == TST_class  &&
+         "Not a class specifier");
+  SU_identifierLoc = Loc;
+  return false;
+}
+
 bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
                            const LangOptions &Lang) {
   // Duplicates turn into warnings pre-C99.
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 0c1080a..3f5f40f 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -796,21 +796,50 @@ bool Parser::MaybeParseTypeSpecifier(DeclSpec &DS, int& isInvalid,
 ///
 void Parser::
 ParseStructDeclaration(DeclSpec &DS,
-                       llvm::SmallVectorImpl<FieldDeclarator> &Fields) {
+                       llvm::SmallVectorImpl<FieldDeclarator> &Fields,
+                       bool AllowUnnamed) {
   if (Tok.is(tok::kw___extension__)) {
     // __extension__ silences extension warnings in the subexpression.
     ExtensionRAIIObject O(Diags);  // Use RAII to do this.
     ConsumeToken();
-    return ParseStructDeclaration(DS, Fields);
+    return ParseStructDeclaration(DS, Fields, true /* AllowUnnamed */);
   }
   
   // Parse the common specifier-qualifiers-list piece.
   SourceLocation DSStart = Tok.getLocation();
   ParseSpecifierQualifierList(DS);
   
-  // If there are no declarators, issue a warning.
+  // If there are no declarators, issue a warning in non gnu mode, or
+  // set up an unnamed structure in gnu mode.
   if (Tok.is(tok::semi)) {
-    Diag(DSStart, diag::w_no_declarators);
+
+    // We filter out 'struct foo;' or 'struct foo {...};' as gcc
+    // does.
+    if (!DS.getStructOrUnionIdentifierLoc().isInvalid() ||
+        !AllowUnnamed) {
+      // This unnamed structure is not legal
+      Diag(DSStart, diag::w_no_declarators);
+    } else {
+
+      // In C++ mode unnamed union are allowed
+      bool isNotLangExtension =
+        (DS.getTypeSpecType() == DeclSpec::TST_union) &&
+        getLang().CPlusPlus;
+
+      if(!isNotLangExtension) {
+        // Explains that this is an extension
+        Diag(DSStart, diag::ext_unnamed_struct_union_class);
+      }
+
+      if(isNotLangExtension || !PP.getLangOptions().NoExtensions) {
+        Fields.push_back(FieldDeclarator(DS));
+        FieldDeclarator &FD = Fields.back();
+        FD.D.SetIdentifier(0, Tok.getLocation());
+      } else {
+        // We didn't do anything. State it.
+        Diag(DSStart, diag::w_no_declarators);
+      }
+    }
     return;
   }
 
@@ -892,7 +921,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
     DeclSpec DS;
     FieldDeclarators.clear();
     if (!Tok.is(tok::at)) {
-      ParseStructDeclaration(DS, FieldDeclarators);
+      ParseStructDeclaration(DS, FieldDeclarators, true /* AllowUnnamed */);
       
       // Convert them all to fields.
       for (unsigned i = 0, e = FieldDeclarators.size(); i != e; ++i) {
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 752d552..ccb8e6d 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -279,6 +279,8 @@ void Parser::ParseClassSpecifier(DeclSpec &DS) {
   const char *PrevSpec = 0;
   if (DS.SetTypeSpecType(TagType, StartLoc, PrevSpec, TagDecl))
     Diag(StartLoc, diag::err_invalid_decl_spec_combination, PrevSpec);
+  else
+    DS.SetStructOrUnionIdentifierLoc(NameLoc);
 }
 
 /// ParseBaseClause - Parse the base-clause of a C++ class [C++ class.derived]. 
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index f6d77cf..fde5d8f 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -310,7 +310,8 @@ void Parser::ParseObjCInterfaceDeclList(DeclTy *interfaceDecl,
       // Parse all the comma separated declarators.
       DeclSpec DS;
       llvm::SmallVector<FieldDeclarator, 8> FieldDeclarators;
-      ParseStructDeclaration(DS, FieldDeclarators);
+      ParseStructDeclaration(DS, FieldDeclarators,
+        false /* don't AllowUnnamed */);
       
       ExpectAndConsume(tok::semi, diag::err_expected_semi_decl_list, "",
                        tok::at);
@@ -875,7 +876,8 @@ void Parser::ParseObjCClassInstanceVariables(DeclTy *interfaceDecl,
     // Parse all the comma separated declarators.
     DeclSpec DS;
     FieldDeclarators.clear();
-    ParseStructDeclaration(DS, FieldDeclarators);
+    ParseStructDeclaration(DS, FieldDeclarators,
+        false /* don't AllowUnnamed */);
     
     // Convert them all to fields.
     for (unsigned i = 0, e = FieldDeclarators.size(); i != e; ++i) {
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index efe481a..4f295f1 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -380,6 +380,9 @@ public:
   FunctionDecl *MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old);
   void CheckForFileScopedRedefinitions(Scope *S, VarDecl *VD);
 
+  /// Helper for dealing with unnamed members of struct/union/class
+  bool CheckNameCollisionBetweenMembers(FieldDecl * newerFD, FieldDecl * olderFD);
+
   /// C++ Overloading.
   bool IsOverload(FunctionDecl *New, Decl* OldD, 
                   OverloadedFunctionDecl::function_iterator &MatchedDecl);
@@ -1055,6 +1058,13 @@ public:
                                SourceLocation LParenLoc,
                                SourceLocation RParenLoc);
 
+  /// Return the Expr that resolves access to unnamed members if
+  /// applicable
+  Expr * ExprThatResolvesUnnamedReference(
+                         ExprTy *BaseExpr, SourceLocation OpLoc,
+                         tok::TokenKind OpKind, SourceLocation MemberLoc,
+                         IdentifierInfo &Member);
+
   /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit
   /// cast.  If there is already an implicit cast, merge into the existing one.
   /// If isLvalue, the result of the cast is an lvalue.
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 6dd9fa7..b6efc75 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -605,11 +605,23 @@ bool Sema::CheckParmsForFunctionDef(FunctionDecl *FD) {
 /// ParsedFreeStandingDeclSpec - This method is invoked when a declspec with
 /// no declarator (e.g. "struct foo;") is parsed.
 Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
+  TagDecl * aTD = dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));;
+  RecordDecl * RD = dyn_cast_or_null<RecordDecl>(static_cast<Decl *>(DS.getTypeRep()));;
+  if(RD) {
+    // Emit error on unnamed struct/union/class outside of a
+    // struct/union/class
+    SourceLocation loc = DS.getStructOrUnionIdentifierLoc();
+    if(RD->getIdentifier() == 0 && loc.isInvalid()) {
+      SourceRange range = DS.getSourceRange();
+      Diag(range.getBegin(), diag::w_no_declarators);
+      return NULL;
+    }
+  }
   // TODO: emit error on 'int;' or 'const enum foo;'.
   // TODO: emit error on 'typedef int;'
   // if (!DS.isMissingDeclaratorOk()) Diag(...);
   
-  return dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
+  return aTD;
 }
 
 bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) {  
@@ -2633,6 +2645,60 @@ Sema::DeclTy *Sema::ActOnIvar(Scope *S,
   return NewID;
 }
 
+
+// HasNameCollisionBetweenMember - This utility function finds
+// collision between unnamed members.
+// This only makes sense with AllowStructOrUnionInStructOrUnion
+
+// Small utility that could be moved to FieldDecl class eventually
+static inline RecordDecl * getRecordDecl(FieldDecl * FD) {
+  const RecordType * rt = FD->getType()->getAsRecordType();
+  if (!rt) return NULL;
+  RecordDecl * rd = rt->getDecl();
+  if (!rd) return NULL;
+  return rd;
+}
+
+bool
+Sema::CheckNameCollisionBetweenMembers(FieldDecl * newerFD, FieldDecl * olderFD) {
+  bool hasCollision = false;
+
+  if (newerFD->getIdentifier() == olderFD->getIdentifier() &&
+      newerFD->getIdentifier() != 0) {
+
+      // This is not an unnamed FD, and the identifier match:
+      hasCollision = true; // Collision!
+
+      Diag(newerFD->getLocation(), diag::err_redefinition,
+           newerFD->getName());
+      Diag(olderFD->getLocation(), diag::err_previous_declaration,
+           olderFD->getName());
+
+  } else if(newerFD->getIdentifier() == 0) {
+    RecordDecl * newerRD = getRecordDecl(newerFD);
+    if (newerRD) {
+      // This is an unnamed record. Check that it does not collide
+      for(int i = 0; i < newerRD->getNumMembers(); i++) {
+        FieldDecl * newerRDSubMember = newerRD->getMember(i);
+        bool ret = CheckNameCollisionBetweenMembers(newerRDSubMember, olderFD);
+        if (!hasCollision) hasCollision = ret;
+      }
+    }
+  } else if(olderFD->getIdentifier() == 0) {
+    RecordDecl * olderRD = getRecordDecl(olderFD);
+    if (olderRD) {
+      // This is an unnamed record. Check that it does not collide
+      for(int i = 0; i < olderRD->getNumMembers(); i++) {
+        FieldDecl * olderRDSubMember = olderRD->getMember(i);
+        bool ret = CheckNameCollisionBetweenMembers(newerFD, olderRDSubMember);
+        if (!hasCollision) hasCollision = ret;
+      }
+    }
+  }
+
+  return hasCollision;
+}
+
 void Sema::ActOnFields(Scope* S,
                        SourceLocation RecLoc, DeclTy *RecDecl,
                        DeclTy **Fields, unsigned NumFields,
@@ -2761,9 +2827,36 @@ void Sema::ActOnFields(Scope* S,
         continue;
       }
       ++NumNamedMembers;
+    } else {
+      // [GNU] Anonymous members: if this is a struct/union/class we need
+      // to check if there is no sub-member name collision
+      // This only makes sense with AllowStructOrUnionInStructOrUnion
+
+      // This makes sense only with a record
+      if (getRecordDecl(FD)) {
+        bool hasCollision = false;
+        for (unsigned j = 0; j != NumFields; ++j) {
+          if (i == j) continue; // We don't want to check against ourselves
+          
+          FieldDecl *otherFD;
+          otherFD = cast_or_null<FieldDecl>(static_cast<Decl*>(Fields[j]));
+
+          // If this is an other unnamed RD we may have already done the job
+          if (otherFD->getIdentifier() == 0 && getRecordDecl(otherFD)) {
+               if(i < j) continue; // Already done
+          }
+          FieldDecl *newerFD = i > j ? FD : otherFD;
+          FieldDecl *olderFD = i > j ? otherFD : FD;
+
+          bool ret = CheckNameCollisionBetweenMembers(newerFD, olderFD);
+          // Continue in order to have full error reporting.
+          if (!hasCollision) hasCollision = ret;
+        }
+        if (hasCollision) continue;
+      }
     }
   }
- 
+
   // Okay, we successfully defined 'Record'.
   if (Record) {
     Record->defineBody(Context, &RecFields[0], RecFields.size());
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index c9f311a..ffbec99 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -910,6 +910,49 @@ static IdentifierInfo *constructSetterName(IdentifierTable &Idents,
   return Setter;
 }
 
+/// ExprThatResolvesUnnamedAccess - This will return
+/// an Expr that takes in account unnamed structure
+/// access
+Expr * Sema::ExprThatResolvesUnnamedReference(
+                         ExprTy *Base, SourceLocation OpLoc,
+                         tok::TokenKind OpKind, SourceLocation MemberLoc,
+                         IdentifierInfo &Member) {
+  Expr *BaseExpr = static_cast<Expr *>(Base);
+  QualType BaseType = BaseExpr->getType();
+  if (OpKind == tok::arrow) {
+    if (const PointerType *PT = BaseType->getAsPointerType())
+      BaseType = PT->getPointeeType();
+  }
+  const RecordType *RTy = BaseType->getAsRecordType();
+  if (!RTy) return NULL;
+  RecordDecl *RDecl = RTy->getDecl();
+  if (RTy->isIncompleteType()) return NULL;
+
+  for(int i = 0; i < RDecl->getNumMembers(); i++) {
+    FieldDecl *MemberDecl = RDecl->getMember(i);
+    if (MemberDecl->getIdentifier() == &Member ||
+        MemberDecl->getIdentifier() == 0) {
+      // Figure out the type of the member; see C99 6.5.2.3p3
+      // FIXME: Handle address space modifiers
+      QualType MemberType = MemberDecl->getType();
+      unsigned combinedQualifiers =
+          MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers();
+      MemberType = MemberType.getQualifiedType(combinedQualifiers);
+
+      MemberExpr * ME = new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl,
+                            MemberLoc, MemberType);
+      if (MemberDecl->getIdentifier() == &Member)
+        return ME;
+
+      // This is an unnamed struct/union/class. Recurse through members.
+      Expr * ret = ExprThatResolvesUnnamedReference(ME, OpLoc, tok::period, MemberLoc,
+                   Member);
+      if (ret) return ret;
+    }
+  }
+  return NULL; // Not found
+}
+
 Action::ExprResult Sema::
 ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc,
                          tok::TokenKind OpKind, SourceLocation MemberLoc,
@@ -940,21 +983,16 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc,
     if (RTy->isIncompleteType())
       return Diag(OpLoc, diag::err_typecheck_incomplete_tag, RDecl->getName(),
                   BaseExpr->getSourceRange());
+    Expr * expr = ExprThatResolvesUnnamedReference(BaseExpr, OpLoc, OpKind,
+                  MemberLoc, Member);
+
     // The record definition is complete, now make sure the member is valid.
-    FieldDecl *MemberDecl = RDecl->getMember(&Member);
-    if (!MemberDecl)
+    if (!expr)
+    {
       return Diag(MemberLoc, diag::err_typecheck_no_member, Member.getName(),
                   BaseExpr->getSourceRange());
-
-    // Figure out the type of the member; see C99 6.5.2.3p3
-    // FIXME: Handle address space modifiers
-    QualType MemberType = MemberDecl->getType();
-    unsigned combinedQualifiers =
-        MemberType.getCVRQualifiers() | BaseType.getCVRQualifiers();
-    MemberType = MemberType.getQualifiedType(combinedQualifiers);
-
-    return new MemberExpr(BaseExpr, OpKind == tok::arrow, MemberDecl,
-                          MemberLoc, MemberType);
+    }
+    return expr;
   }
   
   // Handle access to Objective-C instance variables, such as "Obj->ivar" and
diff --git a/test/Sema/unnamed-struct-in-struct-2.c b/test/Sema/unnamed-struct-in-struct-2.c
new file mode 100644
index 0000000..5b80096
--- /dev/null
+++ b/test/Sema/unnamed-struct-in-struct-2.c
@@ -0,0 +1,55 @@
+// RUN: not clang -fsyntax-only -verify -std=gnu99 %s
+
+/// Previous declaration checking.
+
+struct bar {
+    int a; // expected-error {{previous declaration is here}}
+    struct {
+       int a; // expected-error {{redefinition of 'a'}}
+       int b; // expected-error {{previous declaration is here}}
+    };
+    int b; // expected-error {{redefinition of 'b'}}
+};
+
+struct foo {
+    struct {
+        int a; // expected-error {{previous declaration is here}}
+        union { float b; }; // expected-error {{previous declaration is here}}
+    };
+    struct {
+        int a; // expected-error {{redefinition of 'a'}}
+        int b; // expected-error {{redefinition of 'b'}} expected-error {{previous declaration is here}}
+    };
+    int b; // expected-error {{redefinition of 'b'}}
+};
+
+void dummy(void) {
+    struct bar a;
+    a.a = 0;
+    a.b = 0;
+}
+
+/// Unnamed non legal structure warnings
+
+struct { // expected-warning {{declaration does not declare anything}}
+    int a;
+};
+
+struct barer {
+    int a;
+    struct foo {int a,b;};  // expected-warning {{declaration does not declare anything}}
+    struct fooer; // expected-warning {{declaration does not declare anything}}
+};
+
+struct fooer {int a,b;};
+
+void moredummy(void) {
+   struct barer a;
+   a.b = 0; // expected-error {{no member named 'b'}}
+    union { // expected-warning {{declaration does not declare anything}}
+        int a;
+        int a;
+    };
+    a = 0; // expected-error {{use of undeclared identifier 'a'}}
+}
+
diff --git a/test/Sema/unnamed-struct-in-struct.c b/test/Sema/unnamed-struct-in-struct.c
new file mode 100644
index 0000000..c4b8cb7
--- /dev/null
+++ b/test/Sema/unnamed-struct-in-struct.c
@@ -0,0 +1,26 @@
+// RUN: clang -fsyntax-only -std=c99 %s
+
+struct a {
+ int a;
+};
+
+struct s {
+    int a;
+    union {
+        int b;
+        float c;
+    };
+    struct { int d; int e; };
+    struct { int f; int g; int x; };
+} element;
+
+
+void dummy(void) {
+  element.a = 0;
+  element.b = 0;
+  element.c = 0;
+  element.d = 0;
+  element.e = 0;
+  element.f = 0;
+  element.g = 0;
+}
-- 
1.6.0.1.285.g1070





_______________________________________________
cfe-dev mailing list
cfe-dev@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic