]> granicus.if.org Git - clang/commitdiff
Implement GNU C extension: two types are compatible if they appear
authorPeter Collingbourne <peter@pcc.me.uk>
Sun, 24 Oct 2010 18:30:18 +0000 (18:30 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Sun, 24 Oct 2010 18:30:18 +0000 (18:30 +0000)
as a function argument, one of the types is a transparent union type
and the other type is compatible with a union member

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@117243 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTContext.h
lib/AST/ASTContext.cpp
test/CodeGen/transparent-union.c
test/Sema/transparent-union.c

index 33f522f91b1f670cc2e146a36aac6ed93e3e438a..55169c4101026d7fd40fe6b2b3b638db1c5b255f 100644 (file)
@@ -1290,6 +1290,12 @@ public:
                       bool Unqualified = false);
   QualType mergeFunctionTypes(QualType, QualType, bool OfBlockPointer=false,
                               bool Unqualified = false);
+  QualType mergeFunctionArgumentTypes(QualType, QualType,
+                                      bool OfBlockPointer=false,
+                                      bool Unqualified = false);
+  QualType mergeTransparentUnionType(QualType, QualType,
+                                     bool OfBlockPointer=false,
+                                     bool Unqualified = false);
   
   QualType mergeObjCGCQualifiers(QualType, QualType);
 
index 313c2ea96b6e688b65b7748938997dd7f50ec997..10f875b1b7683ce92559dc0eb0f3cf9891af5b91 100644 (file)
@@ -4674,6 +4674,49 @@ bool ASTContext::typesAreBlockPointerCompatible(QualType LHS, QualType RHS) {
   return !mergeTypes(LHS, RHS, true).isNull();
 }
 
+/// mergeTransparentUnionType - if T is a transparent union type and a member
+/// of T is compatible with SubType, return the merged type, else return
+/// QualType()
+QualType ASTContext::mergeTransparentUnionType(QualType T, QualType SubType,
+                                               bool OfBlockPointer,
+                                               bool Unqualified) {
+  if (const RecordType *UT = T->getAsUnionType()) {
+    RecordDecl *UD = UT->getDecl();
+    if (UD->hasAttr<TransparentUnionAttr>()) {
+      for (RecordDecl::field_iterator it = UD->field_begin(),
+           itend = UD->field_end(); it != itend; ++it) {
+        QualType ET = it->getType();
+        QualType MT = mergeTypes(ET, SubType, OfBlockPointer, Unqualified);
+        if (!MT.isNull())
+          return MT;
+      }
+    }
+  }
+
+  return QualType();
+}
+
+/// mergeFunctionArgumentTypes - merge two types which appear as function
+/// argument types
+QualType ASTContext::mergeFunctionArgumentTypes(QualType lhs, QualType rhs, 
+                                                bool OfBlockPointer,
+                                                bool Unqualified) {
+  // GNU extension: two types are compatible if they appear as a function
+  // argument, one of the types is a transparent union type and the other
+  // type is compatible with a union member
+  QualType lmerge = mergeTransparentUnionType(lhs, rhs, OfBlockPointer,
+                                              Unqualified);
+  if (!lmerge.isNull())
+    return lmerge;
+
+  QualType rmerge = mergeTransparentUnionType(rhs, lhs, OfBlockPointer,
+                                              Unqualified);
+  if (!rmerge.isNull())
+    return rmerge;
+
+  return mergeTypes(lhs, rhs, OfBlockPointer, Unqualified);
+}
+
 QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, 
                                         bool OfBlockPointer,
                                         bool Unqualified) {
@@ -4751,8 +4794,9 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs,
     for (unsigned i = 0; i < lproto_nargs; i++) {
       QualType largtype = lproto->getArgType(i).getUnqualifiedType();
       QualType rargtype = rproto->getArgType(i).getUnqualifiedType();
-      QualType argtype = mergeTypes(largtype, rargtype, OfBlockPointer,
-                                    Unqualified);
+      QualType argtype = mergeFunctionArgumentTypes(largtype, rargtype,
+                                                    OfBlockPointer,
+                                                    Unqualified);
       if (argtype.isNull()) return QualType();
       
       if (Unqualified)
index 9f1cdda18d10757970d717fd138b7103a68bd010..0bdbd577f905b010ad6621365f1278522ec3adf5 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o %t %s
+// RUN: %clang_cc1 -Werror -triple i386-unknown-unknown -emit-llvm -o %t %s
 // RUN: FileCheck < %t %s
 //
 // FIXME: Note that we don't currently get the ABI right here. f0() should be
@@ -12,9 +12,12 @@ void f0(transp_t0 obj);
 
 // CHECK: define void @f1_0(i32* %a0) 
 // CHECK:  call void @f0(%union.anon* byval %{{.*}})
+// CHECK:  call void %{{.*}}(i8* %{{[a-z0-9]*}})
 // CHECK: }
 void f1_0(int *a0) {
+  void (*f0p)(void *) = f0;
   f0(a0);
+  f0p(a0);
 }
 
 void f1_1(int *a0) {
index 27d5c2403b43c808b7f8e2af0cc91df575126ad9..d13b4045a918f482232822a4f700b01d1d4de863 100644 (file)
@@ -17,6 +17,24 @@ void g(int *ip, float *fp, char *cp) {
   tu.ip = ip;
 }
 
+/* Test ability to redeclare a function taking a transparent_union arg
+   with various compatible and incompatible argument types. */
+
+void fip(TU);
+void fip(int *i) {}
+
+void ffp(TU);
+void ffp(float *f) {}
+
+void fvp(TU); // expected-note{{previous declaration is here}}
+void fvp(void *p) {} // expected-error{{conflicting types}}
+
+void fsp(TU); // expected-note{{previous declaration is here}}
+void fsp(short *s) {} // expected-error{{conflicting types}}
+
+void fi(TU); // expected-note{{previous declaration is here}}
+void fi(int i) {} // expected-error{{conflicting types}}
+
 /* 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. */