diff options
-rw-r--r-- | include/clang/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 44 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 55 | ||||
-rw-r--r-- | test/CodeGenCXX/template-instantiation.cpp | 30 | ||||
-rw-r--r-- | test/SemaTemplate/instantiate-init.cpp | 18 |
5 files changed, 145 insertions, 6 deletions
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index fa9e2fe534..ae6d997327 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -808,6 +808,10 @@ public: const PartialDiagnostic &PD); bool RequireCompleteType(SourceLocation Loc, QualType T, unsigned DiagID); + bool RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, + std::pair<SourceLocation, + PartialDiagnostic> Note); + QualType getElaboratedType(ElaboratedTypeKeyword Keyword, const CXXScopeSpec &SS, QualType T); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b89e2bcc80..9f964fab9c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3111,14 +3111,46 @@ static bool CheckObjCTraitOperandConstraints(Sema &S, QualType T, /// \brief Check the constrains on expression operands to unary type expression /// and type traits. /// -/// This is just a convenience wrapper around -/// Sema::CheckUnaryExprOrTypeTraitOperand. +/// Completes any types necessary and validates the constraints on the operand +/// expression. The logic mostly mirrors the type-based overload, but may modify +/// the expression as it completes the type for that expression through template +/// instantiation, etc. bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *Op, UnaryExprOrTypeTrait ExprKind) { - return CheckUnaryExprOrTypeTraitOperand(Op->getType(), - Op->getExprLoc(), - Op->getSourceRange(), - ExprKind); + QualType ExprTy = Op->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(); + + if (ExprKind == UETT_VecStep) + return CheckVecStepTraitOperandType(*this, ExprTy, Op->getExprLoc(), + Op->getSourceRange()); + + // Whitelist some types as extensions + if (!CheckExtensionTraitOperandType(*this, ExprTy, Op->getExprLoc(), + Op->getSourceRange(), ExprKind)) + return false; + + if (RequireCompleteExprType(Op, + PDiag(diag::err_sizeof_alignof_incomplete_type) + << ExprKind << Op->getSourceRange(), + std::make_pair(SourceLocation(), PDiag(0)))) + return true; + + // Completeing the expression's type may have changed it. + ExprTy = Op->getType(); + if (const ReferenceType *Ref = ExprTy->getAs<ReferenceType>()) + ExprTy = Ref->getPointeeType(); + + if (CheckObjCTraitOperandConstraints(*this, ExprTy, Op->getExprLoc(), + Op->getSourceRange(), ExprKind)) + return true; + + return false; } /// \brief Check the constraints on operands to unary expression and type diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a2433cad51..9fccdb11c5 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -3246,6 +3246,61 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, } while ((attrs = next)); } +/// \brief Ensure that the type of the given expression is complete. +/// +/// This routine checks whether the expression \p E has a complete type. If the +/// expression refers to an instantiable construct, that instantiation is +/// performed as needed to complete its type. Furthermore +/// Sema::RequireCompleteType is called for the expression's type (or in the +/// case of a reference type, the referred-to type). +/// +/// \param E The expression whose type is required to be complete. +/// \param PD The partial diagnostic that will be printed out if the type cannot +/// be completed. +/// +/// \returns \c true if the type of \p E is incomplete and diagnosed, \c false +/// otherwise. +bool Sema::RequireCompleteExprType(Expr *E, const PartialDiagnostic &PD, + std::pair<SourceLocation, + PartialDiagnostic> Note) { + QualType T = E->getType(); + + // Fast path the case where the type is already complete. + if (!T->isIncompleteType()) + return false; + + // Incomplete array types may be completed by the initializer attached to + // their definitions. For static data members of class templates we need to + // instantiate the definition to get this initializer and complete the type. + if (T->isIncompleteArrayType()) { + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) { + if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) { + if (Var->isStaticDataMember() && + Var->getInstantiatedFromStaticDataMember()) { + InstantiateStaticDataMemberDefinition(E->getExprLoc(), Var); + // Update the type to the newly instantiated definition's type both + // here and within the expression. + T = Var->getDefinition()->getType(); + E->setType(T); + + // We still go on to try to complete the type independently, as it + // may also require instantiations or diagnostics if it remains + // incomplete. + } + } + } + } + + // FIXME: Are there other cases which require instantiating something other + // than the type to complete the type of an expression? + + // Look through reference types and complete the referred type. + if (const ReferenceType *Ref = T->getAs<ReferenceType>()) + T = Ref->getPointeeType(); + + return RequireCompleteType(E->getExprLoc(), T, PD, Note); +} + /// @brief Ensure that the type T is a complete type. /// /// This routine checks whether the type @p T is complete in any diff --git a/test/CodeGenCXX/template-instantiation.cpp b/test/CodeGenCXX/template-instantiation.cpp index 635e1d2099..cc30af0aba 100644 --- a/test/CodeGenCXX/template-instantiation.cpp +++ b/test/CodeGenCXX/template-instantiation.cpp @@ -1,9 +1,15 @@ // RUN: %clang_cc1 %s -O1 -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// CHECK: @_ZN7PR100011xE = global +// CHECK-NOT: @_ZN7PR100014kBarE = external global i32 +// // CHECK-NOT: @_ZTVN5test118stdio_sync_filebufIwEE = constant // CHECK-NOT: _ZTVN5test315basic_fstreamXXIcEE // CHECK: @_ZTVN5test018stdio_sync_filebufIwEE = unnamed_addr constant +// CHECK: @_ZN7PR100011SIiE3arrE = weak_odr global [3 x i32] +// CHECK-NOT: @_ZN7PR100011SIiE3arr2E = weak_odr global [3 x i32]A + // CHECK-NOT: _ZTVN5test31SIiEE // CHECK-NOT: _ZTSN5test31SIiEE @@ -122,3 +128,27 @@ class B { // CHECK-NOT: _ZN6PR85051AILi0EE1B1fEv template class A<0>; } + +// Ensure that when instantiating initializers for static data members to +// complete their type in an unevaluated context, we *do* emit initializers with +// side-effects, but *don't* emit initializers and variables which are otherwise +// unused in the program. +namespace PR10001 { + template <typename T> struct S { + static const int arr[]; + static const int arr2[]; + static const int x, y; + static int f(); + }; + + extern int foo(); + extern int kBar; + + template <typename T> const int S<T>::arr[] = { 1, 2, foo() }; // possible side effects + template <typename T> const int S<T>::arr2[] = { 1, 2, kBar }; // no side effects + template <typename T> const int S<T>::x = sizeof(arr) / sizeof(arr[0]); + template <typename T> const int S<T>::y = sizeof(arr2) / sizeof(arr2[0]); + template <typename T> int S<T>::f() { return x + y; } + + int x = S<int>::f(); +} diff --git a/test/SemaTemplate/instantiate-init.cpp b/test/SemaTemplate/instantiate-init.cpp index e292aa3c5f..d5711ddf5a 100644 --- a/test/SemaTemplate/instantiate-init.cpp +++ b/test/SemaTemplate/instantiate-init.cpp @@ -55,3 +55,21 @@ namespace PR6657 { f0<int>(); } } + +// Instantiate out-of-line definitions of static data members which complete +// types through an initializer even when the only use of the member that would +// cause instantiation is in an unevaluated context, but one requiring its +// complete type. +namespace PR10001 { + template <typename T> struct S { + static const int arr[]; + static const int x; + static int f(); + }; + + template <typename T> const int S<T>::arr[] = { 1, 2, 3 }; + template <typename T> const int S<T>::x = sizeof(arr) / sizeof(arr[0]); + template <typename T> int S<T>::f() { return x; } + + int x = S<int>::f(); +} |