From: John McCall Date: Mon, 6 May 2013 07:40:34 +0000 (+0000) Subject: Require the containing type to be complete when we see X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=10f6f065456a2cfb6c2ab5dfedefb930e5e52e9d;p=clang Require the containing type to be complete when we see __alignof__ of a field. This problem can only happen in C++11. Also do some petty optimizations. rdar://13784901 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181185 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 23e4edf09d..bc0640eaa7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4054,6 +4054,8 @@ def err_sizeof_alignof_incomplete_type : Error< "incomplete type %1">; def err_sizeof_alignof_bitfield : Error< "invalid application of '%select{sizeof|alignof}0' to bit-field">; +def err_alignof_member_of_incomplete_type : Error< + "invalid application of 'alignof' to a field of a class still being defined">; def err_vecstep_non_scalar_vector_type : Error< "'vec_step' requires built-in scalar or vector type, %0 invalid">; def err_offsetof_incomplete_type : Error< diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 95bfd63f4d..2b9d13cec0 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -5915,6 +5915,10 @@ CharUnits IntExprEvaluator::GetAlignOfType(QualType T) { CharUnits IntExprEvaluator::GetAlignOfExpr(const Expr *E) { E = E->IgnoreParens(); + // The kinds of expressions that we have special-case logic here for + // should be kept up to date with the special checks for those + // expressions in Sema. + // alignof decl is always accepted, even if it doesn't make sense: we default // to 1 in those cases. if (const DeclRefExpr *DRE = dyn_cast(E)) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d9ef5b93ad..18c1d8fb32 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3165,13 +3165,7 @@ static void warnOnSizeofOnArrayDecay(Sema &S, SourceLocation Loc, QualType T, bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind) { QualType ExprTy = E->getType(); - - // C++ [expr.sizeof]p2: "When applied to a reference or a reference type, - // the result is the size of the referenced type." - // C++ [expr.alignof]p3: "When alignof is applied to a reference type, the - // result shall be the alignment of the referenced type." - if (const ReferenceType *Ref = ExprTy->getAs()) - ExprTy = Ref->getPointeeType(); + assert(!ExprTy->isReferenceType()); if (ExprKind == UETT_VecStep) return CheckVecStepTraitOperandType(*this, ExprTy, E->getExprLoc(), @@ -3187,10 +3181,9 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, ExprKind, E->getSourceRange())) return true; - // Completeing the expression's type may have changed it. + // Completing the expression's type may have changed it. ExprTy = E->getType(); - if (const ReferenceType *Ref = ExprTy->getAs()) - ExprTy = Ref->getPointeeType(); + assert(!ExprTy->isReferenceType()); if (CheckObjCTraitOperandConstraints(*this, ExprTy, E->getExprLoc(), E->getSourceRange(), ExprKind)) @@ -3275,25 +3268,67 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, static bool CheckAlignOfExpr(Sema &S, Expr *E) { E = E->IgnoreParens(); - // alignof decl is always ok. - if (isa(E)) - return false; - // Cannot know anything else if the expression is dependent. if (E->isTypeDependent()) return false; - if (E->getBitField()) { + if (E->getObjectKind() == OK_BitField) { S.Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 1 << E->getSourceRange(); return true; } - // Alignment of a field access is always okay, so long as it isn't a - // bit-field. - if (MemberExpr *ME = dyn_cast(E)) - if (isa(ME->getMemberDecl())) + ValueDecl *D = 0; + if (DeclRefExpr *DRE = dyn_cast(E)) { + D = DRE->getDecl(); + } else if (MemberExpr *ME = dyn_cast(E)) { + D = ME->getMemberDecl(); + } + + // If it's a field, require the containing struct to have a + // complete definition so that we can compute the layout. + // + // This requires a very particular set of circumstances. For a + // field to be contained within an incomplete type, we must in the + // process of parsing that type. To have an expression refer to a + // field, it must be an id-expression or a member-expression, but + // the latter are always ill-formed when the base type is + // incomplete, including only being partially complete. An + // id-expression can never refer to a field in C because fields + // are not in the ordinary namespace. In C++, an id-expression + // can implicitly be a member access, but only if there's an + // implicit 'this' value, and all such contexts are subject to + // delayed parsing --- except for trailing return types in C++11. + // And if an id-expression referring to a field occurs in a + // context that lacks a 'this' value, it's ill-formed --- except, + // agian, in C++11, where such references are allowed in an + // unevaluated context. So C++11 introduces some new complexity. + // + // For the record, since __alignof__ on expressions is a GCC + // extension, GCC seems to permit this but always gives the + // nonsensical answer 0. + // + // We don't really need the layout here --- we could instead just + // directly check for all the appropriate alignment-lowing + // attributes --- but that would require duplicating a lot of + // logic that just isn't worth duplicating for such a marginal + // use-case. + if (FieldDecl *FD = dyn_cast_or_null(D)) { + // Fast path this check, since we at least know the record has a + // definition if we can find a member of it. + if (!FD->getParent()->isCompleteDefinition()) { + S.Diag(E->getExprLoc(), diag::err_alignof_member_of_incomplete_type) + << E->getSourceRange(); + return true; + } + + // Otherwise, if it's a field, and the field doesn't have + // reference type, then it must have a complete type (or be a + // flexible array member, which we explicitly want to + // white-list anyway), which makes the following checks trivial. + if (!FD->getType()->isReferenceType()) return false; + } return S.CheckUnaryExprOrTypeTraitOperand(E, UETT_AlignOf); } diff --git a/test/SemaCXX/alignof.cpp b/test/SemaCXX/alignof.cpp new file mode 100644 index 0000000000..a9de1ad07c --- /dev/null +++ b/test/SemaCXX/alignof.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +// rdar://13784901 + +struct S0 { + int x; + static const int test0 = __alignof__(x); // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} + static const int test1 = __alignof__(S0::x); // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} + auto test2() -> char(&)[__alignof__(x)]; // expected-error {{invalid application of 'alignof' to a field of a class still being defined}} +}; + +struct S1; // expected-note 5 {{forward declaration}} +extern S1 s1; +const int test3 = __alignof__(s1); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + +struct S2 { + S2(); + S1 &s; + int x; + + int test4 = __alignof__(x); // ok + int test5 = __alignof__(s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} +}; + +const int test6 = __alignof__(S2::x); +const int test7 = __alignof__(S2::s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + +// Arguably, these should fail like the S1 cases do: the alignment of +// 's2.x' should depend on the alignment of both x-within-S2 and +// s2-within-S3 and thus require 'S3' to be complete. If we start +// doing the appropriate recursive walk to do that, we should make +// sure that these cases don't explode. +struct S3 { + S2 s2; + + static const int test8 = __alignof__(s2.x); + static const int test9 = __alignof__(s2.s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + auto test10() -> char(&)[__alignof__(s2.x)]; + static const int test11 = __alignof__(S3::s2.x); + static const int test12 = __alignof__(S3::s2.s); // expected-error {{invalid application of 'alignof' to an incomplete type 'S1'}} + auto test13() -> char(&)[__alignof__(s2.x)]; +}; + +// Same reasoning as S3. +struct S4 { + union { + int x; + }; + static const int test0 = __alignof__(x); + static const int test1 = __alignof__(S0::x); + auto test2() -> char(&)[__alignof__(x)]; +};