diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5939,9 +5939,9 @@ def err_opencl_sizeof_alignof_type : Error< "invalid application of '%sub{select_unary_expr_or_type_trait_kind}0' " "to a void type">; -def err_sizeof_alignof_incomplete_type : Error< +def err_sizeof_alignof_incomplete_or_sizeless_type : Error< "invalid application of '%sub{select_unary_expr_or_type_trait_kind}0' " - "to an incomplete type %1">; + "to %select{an incomplete|sizeless}1 type %2">; def err_sizeof_alignof_function_type : Error< "invalid application of '%sub{select_unary_expr_or_type_trait_kind}0' " "to a function type">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1739,6 +1739,7 @@ static SourceRange getPrintable(TypeLoc TL) { return TL.getSourceRange();} template class BoundTypeDiagnoser : public TypeDiagnoser { + protected: unsigned DiagID; std::tuple Args; @@ -1763,6 +1764,37 @@ } }; + /// A derivative of BoundTypeDiagnoser for which the diagnostic's type + /// parameter is preceded by a 0/1 enum that is 1 if the type is sizeless. + /// For example, a diagnostic with no other parameters would generally have + /// the form "...%select{incomplete|sizeless}0 type %1...". + template + class SizelessTypeDiagnoser : public BoundTypeDiagnoser { + public: + SizelessTypeDiagnoser(unsigned DiagID, const Ts &... Args) + : BoundTypeDiagnoser(DiagID, Args...) {} + + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + const SemaDiagnosticBuilder &DB = S.Diag(Loc, this->DiagID); + this->emit(DB, std::index_sequence_for()); + DB << T->isSizelessType() << T; + } + }; + + enum class CompleteTypeKind { + /// Apply the normal rules for complete types. In particular, + /// treat all sizeless types as incomplete. + Normal, + + /// Relax the normal rules for complete types so that they include + /// sizeless built-in types. + AcceptSizeless, + + // FIXME: Eventually we should flip the default to Normal and opt in + // to AcceptSizeless rather than opt out of it. + Default = AcceptSizeless + }; + private: /// Methods for marking which expressions involve dereferencing a pointer /// marked with the 'noderef' attribute. Expressions are checked bottom up as @@ -1776,7 +1808,7 @@ void CheckMemberAccessOfNoDeref(const MemberExpr *E); bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, - TypeDiagnoser *Diagnoser); + CompleteTypeKind Kind, TypeDiagnoser *Diagnoser); struct ModuleScope { SourceLocation BeginLoc; @@ -1867,13 +1899,22 @@ bool isUsualDeallocationFunction(const CXXMethodDecl *FD); - bool isCompleteType(SourceLocation Loc, QualType T) { - return !RequireCompleteTypeImpl(Loc, T, nullptr); + bool isCompleteType(SourceLocation Loc, QualType T, + CompleteTypeKind Kind = CompleteTypeKind::Default) { + return !RequireCompleteTypeImpl(Loc, T, Kind, nullptr); } bool RequireCompleteType(SourceLocation Loc, QualType T, - TypeDiagnoser &Diagnoser); + CompleteTypeKind Kind, TypeDiagnoser &Diagnoser); + bool RequireCompleteType(SourceLocation Loc, QualType T, + CompleteTypeKind Kind, unsigned DiagID); + bool RequireCompleteType(SourceLocation Loc, QualType T, - unsigned DiagID); + TypeDiagnoser &Diagnoser) { + return RequireCompleteType(Loc, T, CompleteTypeKind::Default, Diagnoser); + } + bool RequireCompleteType(SourceLocation Loc, QualType T, unsigned DiagID) { + return RequireCompleteType(Loc, T, CompleteTypeKind::Default, DiagID); + } template bool RequireCompleteType(SourceLocation Loc, QualType T, unsigned DiagID, @@ -1882,14 +1923,29 @@ return RequireCompleteType(Loc, T, Diagnoser); } + template + bool RequireCompleteSizedType(SourceLocation Loc, QualType T, unsigned DiagID, + const Ts &... Args) { + SizelessTypeDiagnoser Diagnoser(DiagID, Args...); + return RequireCompleteType(Loc, T, CompleteTypeKind::Normal, Diagnoser); + } + void completeExprArrayBound(Expr *E); - bool RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser); + bool RequireCompleteExprType(Expr *E, CompleteTypeKind Kind, + TypeDiagnoser &Diagnoser); bool RequireCompleteExprType(Expr *E, unsigned DiagID); template bool RequireCompleteExprType(Expr *E, unsigned DiagID, const Ts &...Args) { BoundTypeDiagnoser Diagnoser(DiagID, Args...); - return RequireCompleteExprType(E, Diagnoser); + return RequireCompleteExprType(E, CompleteTypeKind::Default, Diagnoser); + } + + template + bool RequireCompleteSizedExprType(Expr *E, unsigned DiagID, + const Ts &... Args) { + SizelessTypeDiagnoser Diagnoser(DiagID, Args...); + return RequireCompleteExprType(E, CompleteTypeKind::Normal, Diagnoser); } bool RequireLiteralType(SourceLocation Loc, QualType T, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3969,14 +3969,15 @@ // be complete (and will attempt to complete it if it's an array of unknown // bound). if (ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf) { - if (RequireCompleteType(E->getExprLoc(), - Context.getBaseElementType(E->getType()), - diag::err_sizeof_alignof_incomplete_type, ExprKind, - E->getSourceRange())) + if (RequireCompleteSizedType( + E->getExprLoc(), Context.getBaseElementType(E->getType()), + diag::err_sizeof_alignof_incomplete_or_sizeless_type, ExprKind, + E->getSourceRange())) return true; } else { - if (RequireCompleteExprType(E, diag::err_sizeof_alignof_incomplete_type, - ExprKind, E->getSourceRange())) + if (RequireCompleteSizedExprType( + E, diag::err_sizeof_alignof_incomplete_or_sizeless_type, ExprKind, + E->getSourceRange())) return true; } @@ -4073,9 +4074,9 @@ ExprKind)) return false; - if (RequireCompleteType(OpLoc, ExprType, - diag::err_sizeof_alignof_incomplete_type, - ExprKind, ExprRange)) + if (RequireCompleteSizedType( + OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type, + ExprKind, ExprRange)) return true; if (ExprType->isFunctionType()) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7892,12 +7892,14 @@ /// case of a reference type, the referred-to type). /// /// \param E The expression whose type is required to be complete. +/// \param Kind Selects which completeness rules should be applied. /// \param Diagnoser The object that will emit a diagnostic if the type is /// incomplete. /// /// \returns \c true if the type of \p E is incomplete and diagnosed, \c false /// otherwise. -bool Sema::RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser) { +bool Sema::RequireCompleteExprType(Expr *E, CompleteTypeKind Kind, + TypeDiagnoser &Diagnoser) { QualType T = E->getType(); // Incomplete array types may be completed by the initializer attached to @@ -7912,12 +7914,12 @@ // FIXME: Are there other cases which require instantiating something other // than the type to complete the type of an expression? - return RequireCompleteType(E->getExprLoc(), T, Diagnoser); + return RequireCompleteType(E->getExprLoc(), T, Kind, Diagnoser); } bool Sema::RequireCompleteExprType(Expr *E, unsigned DiagID) { BoundTypeDiagnoser<> Diagnoser(DiagID); - return RequireCompleteExprType(E, Diagnoser); + return RequireCompleteExprType(E, CompleteTypeKind::Default, Diagnoser); } /// Ensure that the type T is a complete type. @@ -7935,11 +7937,14 @@ /// /// @param T The type that this routine is examining for completeness. /// +/// @param Kind Selects which completeness rules should be applied. +/// /// @returns @c true if @p T is incomplete and a diagnostic was emitted, /// @c false otherwise. bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, + CompleteTypeKind Kind, TypeDiagnoser &Diagnoser) { - if (RequireCompleteTypeImpl(Loc, T, &Diagnoser)) + if (RequireCompleteTypeImpl(Loc, T, Kind, &Diagnoser)) return true; if (const TagType *Tag = T->getAs()) { if (!Tag->getDecl()->isCompleteDefinitionRequired()) { @@ -8088,6 +8093,7 @@ /// The implementation of RequireCompleteType bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, + CompleteTypeKind Kind, TypeDiagnoser *Diagnoser) { // FIXME: Add this assertion to make sure we always get instantiation points. // assert(!Loc.isInvalid() && "Invalid location in RequireCompleteType"); @@ -8101,7 +8107,7 @@ if (!MPTy->getClass()->isDependentType()) { if (getLangOpts().CompleteMemberPointers && !MPTy->getClass()->getAsCXXRecordDecl()->isBeingDefined() && - RequireCompleteType(Loc, QualType(MPTy->getClass(), 0), + RequireCompleteType(Loc, QualType(MPTy->getClass(), 0), Kind, diag::err_memptr_incomplete)) return true; @@ -8115,7 +8121,9 @@ } NamedDecl *Def = nullptr; - bool Incomplete = T->isIncompleteType(&Def); + bool AcceptSizeless = (Kind == CompleteTypeKind::AcceptSizeless); + bool Incomplete = (T->isIncompleteType(&Def) || + (!AcceptSizeless && T->isSizelessBuiltinType())); // Check that any necessary explicit specializations are visible. For an // enum, we just need the declaration, so don't check this. @@ -8169,7 +8177,7 @@ // If the external source completed the type, go through the motions // again to ensure we're allowed to use the completed type. if (!T->isIncompleteType()) - return RequireCompleteTypeImpl(Loc, T, Diagnoser); + return RequireCompleteTypeImpl(Loc, T, Kind, Diagnoser); } } @@ -8221,7 +8229,7 @@ // instantiation produced an error, so that repeated calls to this // function give consistent answers. if (!T->isIncompleteType()) - return RequireCompleteTypeImpl(Loc, T, Diagnoser); + return RequireCompleteTypeImpl(Loc, T, Kind, Diagnoser); } } @@ -8254,9 +8262,9 @@ } bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, - unsigned DiagID) { + CompleteTypeKind Kind, unsigned DiagID) { BoundTypeDiagnoser<> Diagnoser(DiagID); - return RequireCompleteType(Loc, T, Diagnoser); + return RequireCompleteType(Loc, T, Kind, Diagnoser); } /// Get diagnostic %select index for tag kind for diff --git a/clang/test/Sema/aarch64-sve-types.c b/clang/test/Sema/aarch64-sve-types.c --- a/clang/test/Sema/aarch64-sve-types.c +++ b/clang/test/Sema/aarch64-sve-types.c @@ -1,52 +1,39 @@ // RUN: %clang_cc1 %s -triple aarch64-none-linux-gnu -target-feature +sve -fsyntax-only -verify -// This test is invalid under the sizeless type extension and is a stop-gap -// until that extension is added. The test makes sure that sizeof and -// alignof queries are handled without assertion failures, since at -// present there is nothing to prevent such queries being made. -// -// Under this scheme, sizeof returns 0 for all built-in sizeless types. -// This is compatible with correct usage but it relies on the user being -// careful to avoid constructs that depend directly or indirectly on the -// value of sizeof. (The sizeless type extension avoids this by treating -// such constructs as an error.) - -// expected-no-diagnostics - void f() { - int size_s8[sizeof(__SVInt8_t) == 0 ? 1 : -1]; - int align_s8[__alignof__(__SVInt8_t) == 16 ? 1 : -1]; + int size_s8[sizeof(__SVInt8_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVInt8_t'}} + int align_s8[__alignof__(__SVInt8_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVInt8_t'}} - int size_s16[sizeof(__SVInt16_t) == 0 ? 1 : -1]; - int align_s16[__alignof__(__SVInt16_t) == 16 ? 1 : -1]; + int size_s16[sizeof(__SVInt16_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVInt16_t'}} + int align_s16[__alignof__(__SVInt16_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVInt16_t'}} - int size_s32[sizeof(__SVInt32_t) == 0 ? 1 : -1]; - int align_s32[__alignof__(__SVInt32_t) == 16 ? 1 : -1]; + int size_s32[sizeof(__SVInt32_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVInt32_t'}} + int align_s32[__alignof__(__SVInt32_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVInt32_t'}} - int size_s64[sizeof(__SVInt64_t) == 0 ? 1 : -1]; - int align_s64[__alignof__(__SVInt64_t) == 16 ? 1 : -1]; + int size_s64[sizeof(__SVInt64_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVInt64_t'}} + int align_s64[__alignof__(__SVInt64_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVInt64_t'}} - int size_u8[sizeof(__SVUint8_t) == 0 ? 1 : -1]; - int align_u8[__alignof__(__SVUint8_t) == 16 ? 1 : -1]; + int size_u8[sizeof(__SVUint8_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVUint8_t'}} + int align_u8[__alignof__(__SVUint8_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVUint8_t'}} - int size_u16[sizeof(__SVUint16_t) == 0 ? 1 : -1]; - int align_u16[__alignof__(__SVUint16_t) == 16 ? 1 : -1]; + int size_u16[sizeof(__SVUint16_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVUint16_t'}} + int align_u16[__alignof__(__SVUint16_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVUint16_t'}} - int size_u32[sizeof(__SVUint32_t) == 0 ? 1 : -1]; - int align_u32[__alignof__(__SVUint32_t) == 16 ? 1 : -1]; + int size_u32[sizeof(__SVUint32_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVUint32_t'}} + int align_u32[__alignof__(__SVUint32_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVUint32_t'}} - int size_u64[sizeof(__SVUint64_t) == 0 ? 1 : -1]; - int align_u64[__alignof__(__SVUint64_t) == 16 ? 1 : -1]; + int size_u64[sizeof(__SVUint64_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVUint64_t'}} + int align_u64[__alignof__(__SVUint64_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVUint64_t'}} - int size_f16[sizeof(__SVFloat16_t) == 0 ? 1 : -1]; - int align_f16[__alignof__(__SVFloat16_t) == 16 ? 1 : -1]; + int size_f16[sizeof(__SVFloat16_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVFloat16_t'}} + int align_f16[__alignof__(__SVFloat16_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVFloat16_t'}} - int size_f32[sizeof(__SVFloat32_t) == 0 ? 1 : -1]; - int align_f32[__alignof__(__SVFloat32_t) == 16 ? 1 : -1]; + int size_f32[sizeof(__SVFloat32_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVFloat32_t'}} + int align_f32[__alignof__(__SVFloat32_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVFloat32_t'}} - int size_f64[sizeof(__SVFloat64_t) == 0 ? 1 : -1]; - int align_f64[__alignof__(__SVFloat64_t) == 16 ? 1 : -1]; + int size_f64[sizeof(__SVFloat64_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVFloat64_t'}} + int align_f64[__alignof__(__SVFloat64_t) == 16 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVFloat64_t'}} - int size_b8[sizeof(__SVBool_t) == 0 ? 1 : -1]; - int align_b8[__alignof__(__SVBool_t) == 2 ? 1 : -1]; + int size_b8[sizeof(__SVBool_t) == 0 ? 1 : -1]; // expected-error {{invalid application of 'sizeof' to sizeless type '__SVBool_t'}} + int align_b8[__alignof__(__SVBool_t) == 2 ? 1 : -1]; // expected-error {{invalid application of '__alignof' to sizeless type '__SVBool_t'}} } diff --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c --- a/clang/test/Sema/sizeless-1.c +++ b/clang/test/Sema/sizeless-1.c @@ -12,6 +12,14 @@ typedef svint8_t int8_typedef; typedef svint8_t *int8_ptr_typedef; +int sizeof_int8 = sizeof(svint8_t); // expected-error {{invalid application of 'sizeof' to sizeless type 'svint8_t'}} +int sizeof_int8_var = sizeof(*extern_int8_ptr); // expected-error {{invalid application of 'sizeof' to sizeless type 'svint8_t'}} +int sizeof_int8_var_ptr = sizeof(extern_int8_ptr); + +int alignof_int8 = _Alignof(svint8_t); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} +int alignof_int8_var = _Alignof(*extern_int8_ptr); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} expected-warning {{GNU extension}} +int alignof_int8_var_ptr = _Alignof(extern_int8_ptr); // expected-warning {{GNU extension}} + void pass_int8(svint8_t); // expected-note {{passing argument to parameter here}} svint8_t return_int8(); @@ -43,6 +51,8 @@ svint8_t local_int8; svint16_t local_int16; + int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} + // Using pointers to sizeless data isn't wrong here, but because the // type is incomplete, it doesn't provide any alignment guarantees. _Static_assert(__atomic_is_lock_free(1, &local_int8) == __atomic_is_lock_free(1, incomplete_ptr), ""); diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp --- a/clang/test/SemaCXX/sizeless-1.cpp +++ b/clang/test/SemaCXX/sizeless-1.cpp @@ -17,6 +17,20 @@ typedef svint8_t int8_typedef; typedef svint8_t *int8_ptr_typedef; +int sizeof_int8 = sizeof(svint8_t); // expected-error {{invalid application of 'sizeof' to sizeless type 'svint8_t'}} +int sizeof_int8_var = sizeof(*extern_int8_ptr); // expected-error {{invalid application of 'sizeof' to sizeless type 'svint8_t'}} +int sizeof_int8_var_ptr = sizeof(extern_int8_ptr); + +#if __cplusplus >= 201103L +int alignof_int8 = alignof(svint8_t); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} +int alignof_int8_var = alignof(*extern_int8_ptr); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} expected-warning {{GNU extension}} +int alignof_int8_var_ptr = alignof(extern_int8_ptr); // expected-warning {{GNU extension}} +#else +int alignof_int8 = _Alignof(svint8_t); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} +int alignof_int8_var = _Alignof(*extern_int8_ptr); // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} expected-warning {{GNU extension}} +int alignof_int8_var_ptr = _Alignof(extern_int8_ptr); // expected-warning {{GNU extension}} +#endif + void pass_int8(svint8_t); // expected-note {{no known conversion}} svint8_t return_int8(); @@ -47,6 +61,8 @@ svint8_t local_int8; svint16_t local_int16; + int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}} + // Using pointers to sizeless data isn't wrong here, but because the // type is incomplete, it doesn't provide any alignment guarantees. _Static_assert(__atomic_is_lock_free(1, &local_int8) == __atomic_is_lock_free(1, incomplete_ptr), "");