From: Douglas Gregor Date: Wed, 29 Apr 2009 22:16:16 +0000 (+0000) Subject: Implement semantic analysis for transparent unions. This is largely X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0c74e8a4e8865ec9ebb8efc0af247a3c077236c4;p=clang 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/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index b850958f1e..cce69ca929 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -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; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6604ddef36..098bbd4e68 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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'">; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index faaeb7989e..d04394d94f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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. diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 2df4e36cc9..df4fd4dd43 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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(d); - if (!TD || !TD->getUnderlyingType()->isUnionType()) { + if (TD && TD->getUnderlyingType()->isUnionType()) + RD = TD->getUnderlyingType()->getAsUnionType()->getDecl(); + else + RD = dyn_cast(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) { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 99ed741120..2de1ef300e 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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()) + 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; } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f4b6598d9e..ab66939656 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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 index 0000000000..90ecaadea6 --- /dev/null +++ b/test/Sema/transparent-union.c @@ -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}}