aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2013-05-06 07:40:34 +0000
committerJohn McCall <rjmccall@apple.com>2013-05-06 07:40:34 +0000
commit10f6f065456a2cfb6c2ab5dfedefb930e5e52e9d (patch)
tree6576c188ece19da725ee49abf51aa026366a80dd
parent692eafd2052fb6ca581530d6f3569eea9520a508 (diff)
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
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td2
-rw-r--r--lib/AST/ExprConstant.cpp4
-rw-r--r--lib/Sema/SemaExpr.cpp73
-rw-r--r--test/SemaCXX/alignof.cpp52
4 files changed, 112 insertions, 19 deletions
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<DeclRefExpr>(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<ReferenceType>())
- 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<ReferenceType>())
- 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<DeclRefExpr>(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<MemberExpr>(E))
- if (isa<FieldDecl>(ME->getMemberDecl()))
+ ValueDecl *D = 0;
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ D = DRE->getDecl();
+ } else if (MemberExpr *ME = dyn_cast<MemberExpr>(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<FieldDecl>(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)];
+};