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/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 99ed741..2de1ef3 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -3124,6 +3124,73 @@
   return Incompatible;
 }
 
+/// \brief Constructs a transparent union from an expression that is
+/// used to initialize the transparent union.
+static void ConstructTransparentUnion(ASTContext &C, Expr *&E, 
+                                      QualType UnionType, FieldDecl *Field) {
+  // Build an initializer list that designates the appropriate member
+  // of the transparent union.
+  InitListExpr *Initializer = new (C) InitListExpr(SourceLocation(),
+                                                   &E, 1,
+                                                   SourceLocation());
+  Initializer->setType(UnionType);
+  Initializer->setInitializedFieldInUnion(Field);
+
+  // Build a compound literal constructing a value of the transparent
+  // union type from this initializer list.
+  E = new (C) CompoundLiteralExpr(SourceLocation(), UnionType, Initializer,
+                                  false);
+}
+
+Sema::AssignConvertType
+Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType, Expr *&rExpr) {
+  QualType FromType = rExpr->getType();
+
+  // If the ArgType is a Union type, we want to handle a potential 
+  // transparent_union GCC extension.
+  const RecordType *UT = ArgType->getAsUnionType();
+  if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
+    return Incompatible;
+
+  // The field to initialize within the transparent union.
+  RecordDecl *UD = UT->getDecl();
+  FieldDecl *InitField = 0;
+  // It's compatible if the expression matches any of the fields.
+  for (RecordDecl::field_iterator it = UD->field_begin(Context),
+         itend = UD->field_end(Context);
+       it != itend; ++it) {
+    if (it->getType()->isPointerType()) {
+      // If the transparent union contains a pointer type, we allow:
+      // 1) void pointer
+      // 2) null pointer constant
+      if (FromType->isPointerType())
+        if (FromType->getAsPointerType()->getPointeeType()->isVoidType()) {
+          ImpCastExprToType(rExpr, it->getType());
+          InitField = *it;
+          break;
+        }
+      
+      if (rExpr->isNullPointerConstant(Context)) {
+        ImpCastExprToType(rExpr, it->getType());
+        InitField = *it;
+        break;
+      }
+    }
+
+    if (CheckAssignmentConstraints(it->getType(), rExpr->getType())
+          == Compatible) {
+      InitField = *it;
+      break;
+    }
+  }
+
+  if (!InitField)
+    return Incompatible;
+
+  ConstructTransparentUnion(Context, rExpr, ArgType, InitField);
+  return Compatible;
+}
+
 Sema::AssignConvertType
 Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
   if (getLangOptions().CPlusPlus) {
@@ -3169,7 +3236,7 @@
   // so that we can use references in built-in functions even in C.
   // The getNonReferenceType() call makes sure that the resulting expression
   // does not have reference type.
-  if (rExpr->getType() != lhsType)
+  if (result != Incompatible && rExpr->getType() != lhsType)
     ImpCastExprToType(rExpr, lhsType.getNonReferenceType());
   return result;
 }