r181185 - Require the containing type to be complete when we see

John McCall rjmccall at apple.com
Mon May 6 00:40:35 PDT 2013


Author: rjmccall
Date: Mon May  6 02:40:34 2013
New Revision: 181185

URL: http://llvm.org/viewvc/llvm-project?rev=181185&view=rev
Log:
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

Added:
    cfe/trunk/test/SemaCXX/alignof.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=181185&r1=181184&r2=181185&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May  6 02:40:34 2013
@@ -4054,6 +4054,8 @@ def err_sizeof_alignof_incomplete_type :
   "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<

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=181185&r1=181184&r2=181185&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May  6 02:40:34 2013
@@ -5915,6 +5915,10 @@ CharUnits IntExprEvaluator::GetAlignOfTy
 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))

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=181185&r1=181184&r2=181185&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon May  6 02:40:34 2013
@@ -3165,13 +3165,7 @@ static void warnOnSizeofOnArrayDecay(Sem
 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::CheckUnaryExprOrTypeTraitOper
                               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::CheckUnaryExprOrTypeTraitOper
 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);
 }

Added: cfe/trunk/test/SemaCXX/alignof.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/alignof.cpp?rev=181185&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/alignof.cpp (added)
+++ cfe/trunk/test/SemaCXX/alignof.cpp Mon May  6 02:40:34 2013
@@ -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)];
+};





More information about the cfe-commits mailing list