]> granicus.if.org Git - clang/commitdiff
Implement semantic analysis for transparent unions. This is largely
authorDouglas Gregor <dgregor@apple.com>
Wed, 29 Apr 2009 22:16:16 +0000 (22:16 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 29 Apr 2009 22:16:16 +0000 (22:16 +0000)
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

include/clang/AST/Attr.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/Sema.h
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/Sema/transparent-union.c [new file with mode: 0644]

index b850958f1eaf3e9b3af42fb77c96d99052e77378..cce69ca929b42b4d7529fb8c8fab4a6798d3102b 100644 (file)
@@ -400,7 +400,9 @@ public:
 
   virtual bool isMerged() const { return false; }
 
-  virtual Attr *clone(ASTContext &C) const { return ::new (C) OverloadableAttr; }
+  virtual Attr *clone(ASTContext &C) const { 
+    return ::new (C) OverloadableAttr; 
+  }
 
   static bool classof(const Attr *A) { return A->getKind() == Overloadable; }
   static bool classof(const OverloadableAttr *) { return true; }
index 6604ddef36256f0b8fbe5329a6b083b9d403e1c6..098bbd4e68e93e8b4f6ad77a849c6e2051d1139e 100644 (file)
@@ -415,18 +415,21 @@ def warn_gnu_inline_attribute_requires_inline : Warning<
 
 def warn_attribute_ignored_for_field_of_type : Warning<
   "%0 attribute ignored for field of type %1">;
-def warn_transparent_union_attribute_field_size : Warning<
-  "transparent_union attribute ignored, size of type %0 must match "
-  "type of first field">;
-def warn_transparent_union_attribute_not_difinition : Warning<
-  "transparent_union attribute ignored, union type must be defined">;
+def warn_transparent_union_attribute_field_size_align : Warning<
+  "%select{alignment|size}0 of field %1 (%2 bits) does not match the "
+  "%select{alignment|size}0 of the first field in transparent union; "
+  "transparent_union attribute ignored">;
+def note_transparent_union_first_field_size_align : Note<
+  "%select{alignment|size}0 of first field is %1 bits">;
+def warn_transparent_union_attribute_not_definition : Warning<
+  "transparent_union attribute can only be applied to a union definition; "
+  "attribute ignored">;
 def warn_transparent_union_attribute_floating : Warning<
-  "transparent_union attribute ignored, first field cannot be a floating-point "
-  "or vector type">;
+  "first field of a transparent union cannot have floating point or vector "
+  "type; transparent_union attribute ignored">;
 def warn_transparent_union_attribute_zero_fields : Warning<
-  "transparent_union attribute ignored, the union does not contain any fields">;
-def warn_transparent_union_attribute_not_c : Warning<
-  "transparent_union attribute ignored, attribute is c only">;
+  "transparent union definition must contain at least one field; "
+  "transparent_union attribute ignored">;
 def warn_attribute_type_not_supported : Warning<
   "'%0' attribute argument not supported: %1">;
 def warn_attribute_unknown_visibility : Warning<"unknown visibility '%1'">;
index faaeb7989e50227dd05c956e3281fee9e855b580..d04394d94fd4c22454ccbc5c240285ac23a1a20b 100644 (file)
@@ -2355,11 +2355,17 @@ public:
   /// This routine is only used by the following two methods. C99 6.5.16.
   AssignConvertType CheckAssignmentConstraints(QualType lhs, QualType rhs);
   
-  // CheckSingleAssignmentConstraints - Currently used by ActOnCallExpr,
+  // CheckSingleAssignmentConstraints - Currently used by 
   // CheckAssignmentOperands, and ActOnReturnStmt. Prior to type checking, 
   // this routine performs the default function/array converions.
   AssignConvertType CheckSingleAssignmentConstraints(QualType lhs, 
                                                      Expr *&rExpr);
+
+  // \brief If the lhs type is a transparent union, check whether we
+  // can initialize the transparent union with the given expression.
+  AssignConvertType CheckTransparentUnionArgumentConstraints(QualType lhs, 
+                                                             Expr *&rExpr);
+
   // CheckCompoundAssignmentConstraints - Type check without performing any 
   // conversions. For compound assignments, the "Check...Operands" methods 
   // perform the necessary conversions. 
index 2df4e36cc9544be4121be44107dc3e306a9c63d3..df4fd4dd430909fa3adb87b9536d2e73e1c926af 100644 (file)
@@ -1178,36 +1178,63 @@ static void HandleTransparentUnionAttr(Decl *d, const AttributeList &Attr,
     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;
+  }
+
+  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: Should we do a check for RD->isDefinition()?
+  FieldDecl *FirstField = *Field;
+  QualType FirstType = FirstField->getType();
+  if (FirstType->isFloatingType() || FirstType->isVectorType()) {
+    S.Diag(FirstField->getLocation(), 
+           diag::warn_transparent_union_attribute_floating);
+    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);
+  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) {
index 99ed741120e527a280b7f241a54f9d33fecf5133..2de1ef300e9dee1254d003738bd08b9f465be069 100644 (file)
@@ -3124,6 +3124,73 @@ Sema::CheckAssignmentConstraints(QualType lhsType, QualType rhsType) {
   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 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, Expr *&rExpr) {
   // 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;
 }
index f4b6598d9e2104e5c3d259709430e6002117b17a..ab66939656ec879ee014bddceceb2d4f516e957c 100644 (file)
@@ -1888,9 +1888,13 @@ bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType,
   if (!getLangOptions().CPlusPlus) {
     // In C, argument passing is the same as performing an assignment.
     QualType FromType = From->getType();
+    
     AssignConvertType ConvTy =
       CheckSingleAssignmentConstraints(ToType, From);
-
+    if (ConvTy != Compatible &&
+        CheckTransparentUnionArgumentConstraints(ToType, From) == Compatible)
+      ConvTy = Compatible;
+    
     return DiagnoseAssignmentResult(ConvTy, From->getLocStart(), ToType,
                                     FromType, From, Flavor);
   }
diff --git a/test/Sema/transparent-union.c b/test/Sema/transparent-union.c
new file mode 100644 (file)
index 0000000..90ecaad
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: clang -fsyntax-only -Xclang -verify %s
+typedef union {
+  int *ip;
+  float *fp;
+} TU __attribute__((transparent_union));
+
+void f(TU);
+
+void g(int *ip, float *fp, char *cp) {
+  f(ip);
+  f(fp);
+  f(cp); // expected-error{{incompatible type}}
+  f(0);
+
+  TU tu_ip = ip; // expected-error{{incompatible type}}
+  TU tu;
+  tu.ip = ip;
+}
+
+/* FIXME: we'd like to just use an "int" here and align it differently
+   from the normal "int", but if we do so we lose the alignment
+   information from the typedef within the compiler. */
+typedef struct { int x, y; } __attribute__((aligned(8))) aligned_struct8;
+
+typedef struct { int x, y; } __attribute__((aligned(4))) aligned_struct4;
+typedef union {
+  aligned_struct4 s4; // expected-note{{alignment of first field}}
+  aligned_struct8 s8; // expected-warning{{alignment of field}}
+} TU1 __attribute__((transparent_union));
+
+typedef union {
+  char c; // expected-note{{size of first field is 8 bits}}
+  int i; // expected-warning{{size of field}}
+} TU2 __attribute__((transparent_union));
+
+typedef union {
+  float f; // expected-warning{{floating}}
+} TU3 __attribute__((transparent_union));
+
+typedef union { } TU4 __attribute__((transparent_union)); // expected-warning{{field}}