Implement semantic analysis for transparent unions. This is largely
based on a patch from Anders Johnsen. CodeGen support is incomplete,
in that we do not properly coerce to the first field's type.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@70419 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 2df4e36..df4fd4d 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1178,36 +1178,63 @@
     return;
   }
 
-  // FIXME: This shouldn't be restricted to typedefs
+  // Try to find the underlying union declaration.
+  RecordDecl *RD = 0;
   TypedefDecl *TD = dyn_cast<TypedefDecl>(d);
-  if (!TD || !TD->getUnderlyingType()->isUnionType()) {
+  if (TD && TD->getUnderlyingType()->isUnionType())
+    RD = TD->getUnderlyingType()->getAsUnionType()->getDecl();
+  else
+    RD = dyn_cast<RecordDecl>(d);
+
+  if (!RD || !RD->isUnion()) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
       << "transparent_union" << 1 /*union*/;
     return;
   }
 
-  RecordDecl* RD = TD->getUnderlyingType()->getAsUnionType()->getDecl();
+  if (!RD->isDefinition()) {
+    S.Diag(Attr.getLoc(), 
+        diag::warn_transparent_union_attribute_not_definition);
+    return;
+  }
 
-  // FIXME: Should we do a check for RD->isDefinition()?
+  RecordDecl::field_iterator Field = RD->field_begin(S.Context),
+                          FieldEnd = RD->field_end(S.Context);
+  if (Field == FieldEnd) {
+    S.Diag(Attr.getLoc(), diag::warn_transparent_union_attribute_zero_fields);
+    return;
+  }
 
-  // FIXME: This isn't supposed to be restricted to pointers, but otherwise
-  // we might silently generate incorrect code; see following code
-  for (RecordDecl::field_iterator Field = RD->field_begin(S.Context),
-                               FieldEnd = RD->field_end(S.Context);
-       Field != FieldEnd; ++Field) {
-    if (!Field->getType()->isPointerType()) {
-      S.Diag(Attr.getLoc(), diag::warn_transparent_union_nonpointer);
+  FieldDecl *FirstField = *Field;
+  QualType FirstType = FirstField->getType();
+  if (FirstType->isFloatingType() || FirstType->isVectorType()) {
+    S.Diag(FirstField->getLocation(), 
+           diag::warn_transparent_union_attribute_floating);
+    return;
+  }
+
+  uint64_t FirstSize = S.Context.getTypeSize(FirstType);
+  uint64_t FirstAlign = S.Context.getTypeAlign(FirstType);
+  for (; Field != FieldEnd; ++Field) {
+    QualType FieldType = Field->getType();
+    if (S.Context.getTypeSize(FieldType) != FirstSize ||
+        S.Context.getTypeAlign(FieldType) != FirstAlign) {
+      // Warn if we drop the attribute.
+      bool isSize = S.Context.getTypeSize(FieldType) != FirstSize;
+      unsigned FieldBits = isSize? S.Context.getTypeSize(FieldType) 
+                                 : S.Context.getTypeAlign(FieldType);
+      S.Diag(Field->getLocation(), 
+          diag::warn_transparent_union_attribute_field_size_align)
+        << isSize << Field->getDeclName() << FieldBits;
+      unsigned FirstBits = isSize? FirstSize : FirstAlign;
+      S.Diag(FirstField->getLocation(), 
+             diag::note_transparent_union_first_field_size_align)
+        << isSize << FirstBits;
       return;
     }
   }
 
-  // FIXME: This is a complete hack; we should be properly propagating
-  // transparent_union through Sema.  That said, this is close enough to
-  // correctly compile all the common cases of transparent_union without
-  // errors or warnings
-  QualType NewTy = S.Context.VoidPtrTy;
-  NewTy.addConst();
-  TD->setUnderlyingType(NewTy);
+  RD->addAttr(::new (S.Context) TransparentUnionAttr());
 }
 
 static void HandleAnnotateAttr(Decl *d, const AttributeList &Attr, Sema &S) {