Index: docs/SizelessTypes.rst =================================================================== --- /dev/null +++ docs/SizelessTypes.rst @@ -0,0 +1,568 @@ +============== +Sizeless types +============== + +As an extension, Clang supports the concept of “sizeless” object types in +both C and C++. The types are so called because it is an error to measure +their size directly using ``sizeof`` or indirectly via operations like +pointer arithmetic. + +Forbidding ``sizeof`` and related operations means that the amount of +data that the types contain does not need to be a compile-time constant. +It can instead depend on runtime properties, and for example can adapt +to different hardware configurations. + +Sizeless types are only intended for objects that hold temporary working +data, such as “scalable” or variable-length vectors. They are not +intended for long-term storage and cannot be used in aggregates. + +At present, the only sizeless types that Clang provides are: + +AArch64 SVE vector types + These vector types are built into the compiler under names like + ``__SVInt8_t``, as required by the `Procedure Call Standard for the + Arm® 64-bit Architecture`_. They represent the longest vector of a + particular element type that can be stored in an SVE vector register. + Functions can pass and return these vectors in registers. + + The header file ```` makes the types available under more + user-friendly names like ``svint8_t``. It also provides a set of + intrinsic functions for operating on the types. See the `ARM C + Language Extensions for SVE`_ for more information about these types + and intrinsics. + + .. _Procedure Call Standard for the Arm® 64-bit Architecture: + https://developer.arm.com/docs/ihi0055/latest/ + .. _ARM C Language Extensions for SVE: + https://developer.arm.com/docs/100987/latest + +`ARM C Language Extensions for SVE`_ contains the original specification of +sizeless types, but the description below is intended to be self-contained. + +Outline of the type system changes +================================== + +C and C++ classify object types as “complete” (the size of objects +of that type can be calculated) or “incomplete” (the size of objects +of that type cannot be calculated). There is very little you can do with +a type until it becomes complete. + +This categorization implicitly ties two concepts: whether it is possible +to manipulate objects of a particular type, and whether it is possible +to measure their size (which in C++ must be constant). The key idea +behind the sizeless type extension is to split these concepts apart. + +To do this, the extension classifies types as: + +* “indefinite” (lacking sufficient information to create an object of + that type) or “definite” (having sufficient information) + +* “sized” (will have a measurable size when definite) or “sizeless” + (will never have a measurable size) + +* “incomplete” (lacking sufficient information to determine the size of + objects of that type) or “complete” (having sufficient information) + +where the wording for the final bullet is taken verbatim from the +C standard. All standard types are “sized” (even ``void``, although +it is always indefinite). + +The idea is that “definite” types are as fully-defined as they +ever can be, even if their size is still not known at compile time. +“Complete” is then equivalent to “sized and definite”. + +On its own, this puts sizeless types into a similar position +to incomplete structure types, which is conservatively correct +but severely limits what the types can do. + +The next step is to relax certain rules so that they use the distinction +between “indefinite” and “definite” rather than “incomplete” and “complete”. +The goal of this process is to allow: + +* automatic variables with sizeless type +* function parameters and return values with sizeless type +* use of sizeless types with ``_Generic`` +* pointers to sizeless types +* applying ``typeid`` to a sizeless type +* use of sizeless types with C++ type traits + +In contrast, the following must remain invalid, by keeping the usual rules +for incomplete types unchanged: + +* using ``sizeof``, ``_Alignof`` and ``alignof`` with a sizeless type + (or object of sizeless type) +* creating or accessing arrays that have sizeless type +* doing pointer arithmetic on pointers to sizeless types +* unions or structures with sizeless members +* applying ``_Atomic`` to a sizeless type +* throwing or catching objects of sizeless type +* capturing sizeless objects by value in lambda expressions + +There is also an extra restriction: + +* variables with sizeless type must not have static or thread-local + storage duration + +In practice it is impossible to *define* such variables with incomplete type, +but having an explicit rule means that things like: + +.. code-block:: c + + extern __SVInt8_t foo; + +are outright invalid rather than simply useless (because no other +translation unit could ever define ``foo``). Similarly, without an +explicit rule: + +.. code-block:: c + + __SVInt8_t foo; + +would be a valid tentative definition at the point it occurs and only +become invalid at the end of the translation unit, because ``__SVInt8_t`` +is never completed. + +Edits to the standards +====================== + +Edits to the C standard +----------------------- + +This section specifies the behavior for sizeless types in C, as an edit +to the N1570 draft of C11. + +6.2.5 Types +~~~~~~~~~~~ + +In 6.2.5p1, replace: + + At various points within a translation unit an object type may be + *incomplete* … + +onwards with: + + Object types are further partitioned into *sized* and *sizeless*; all + basic and derived types defined in this standard are sized, but an + implementation may provide additional sizeless types. + +and add two additional clauses: + +* At various points within a translation unit an object type may be + *indefinite* (lacking sufficient information to construct an object + of that type) or *definite* (having sufficient information). + An object type is said to be *complete* if it is both sized and + definite; all other object types are said to be *incomplete*. + Complete types have sufficient information to determine the size + of an object of that type while incomplete types do not. + +* Arrays, structures, unions and enumerated types are always sized, + so for them the term *incomplete* is equivalent to (and used + interchangeably with) the term *indefinite*. + +Change 6.2.5p19 to: + + The void type comprises an empty set of values; it is a sized + indefinite object type that cannot be completed (made definite). + +Replace “incomplete” with “indefinite” and “complete” with “definite” in +6.2.5p37, which describes how a type's state can change throughout a +translation unit. + +6.3.2.1 Lvalues, arrays, and function designators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “indefinite” in 6.3.2.1p1, so that sizeless +definite types are modifiable lvalues. + +Make the same replacement in 6.3.2.1p2, to prevent undefined behavior +when lvalues have sizeless definite type. + +6.5.1.1 Generic selection +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete object type” with “definite object type” in 6.5.1.1p2, +so that the type name in a generic association can be a sizeless definite +type. + +6.5.2.2 Function calls +~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete object type” with “definite object type” in 6.5.2.2p1, +so that functions can return sizeless definite types. + +Make the same change in 6.5.2.2p4, so that arguments can also have +sizeless definite type. + +6.5.2.5 Compound literals +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete object type” with “definite object type” in 6.5.2.5p1, +so that compound literals can have sizeless definite type. + +6.7 Declarations +~~~~~~~~~~~~~~~~ + +Insert the following new clause after 6.7p4: + +* If an identifier for an object does not have automatic storage duration, + its type must be sized rather than sizeless. + +Replace “complete” with “definite” in 6.7p7, which describes when the +type of an object becomes definite. + +6.7.6.3 Function declarators (including prototypes) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete type” with “indefinite type” in 6.7.6.3p4, so that +parameters can also have sizeless definite type. + +Make the same change in 6.7.6.3p12, which allows even indefinite types +to be function parameters if no function definition is present. + +6.7.9 Initialization +~~~~~~~~~~~~~~~~~~~~ + +Replace “complete object type” with “definite object type” in 6.7.9p3, +to allow initialization of identifiers with sizeless definite type. + +6.9.1 Function definitions +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete object type” with “definite object type” in 6.9.1p3, +so that functions can return sizeless definite types. + +Make the same change in 6.9.1p7, so that adjusted parameter types can be +sizeless definite types. + +J.2 Undefined behavior +~~~~~~~~~~~~~~~~~~~~~~ + +Update the entries that refer to the clauses above. + +Edits to the C++ standard +------------------------- + +This section specifies the behavior for sizeless types in C++, +as an edit to the N3797 draft of C++17. + +3.1 Declarations and definitions [basic.def] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “indefinite” in [basic.def]p5, so that definitions +of an object can give it sizeless definite type. Add a further clause +after [basic.def]p5: + +* A program is ill-formed if any declaration of an object gives it both + a sizeless type and either static or thread-local storage duration. + +3.9 Types [basic.types] +~~~~~~~~~~~~~~~~~~~~~~~ + +Replace [basic.types]p5 with: + + A class that has been declared but not defined, an enumeration type + in certain contexts (7.2), or an array of unknown size or of + indefinite element type, is an indefinite object type.45) + Indefinite object types and the void types are indefinite types (3.9.1). + Objects shall not be defined to have an indefinite type. + +and add three additional clauses: + +* Object and void types are further partitioned into *sized* and *sizeless*; + all basic and derived types defined in this standard are sized, but an + implementation may provide additional sizeless types. + +* An object or void type is said to be *complete* if it is both sized and + definite; all other object and void types are said to be *incomplete*. + The term *completely-defined object type* is synonymous with *complete + object type*. + +* Arrays, class types and enumeration types are always sized, so for + them the term *incomplete* is equivalent to (and used interchangeably + with) the term *indefinite*. + +(Note that the wording of footnote 45 continues to apply as-is.) + +Also replace “incomplete” with “indefinite” in the forward reference +in [basic.types]p7. + +3.9.1 Fundamental Types [basic.fundamental] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In [basic.fundamental]p9, replace the second sentence with: + + The void type is a sized indefinite type that cannot be completed + (made definite). + +leaving the rest of the clause unchanged. + +3.9.2. Compound Types [basic.compound] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this part of [basic.compound]p3: + + Pointers to incomplete types are allowed although there are + restrictions on what can be done with them … + +add “(including indefinite types)” after “incomplete types”. + +3.10 Lvalues and rvalues [basic.lval] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” and “incomplete” with “indefinite” in +[basic.lval]p4, so that prvalues can have definite type and (in contrast) +glvalues can have indefinite type. + +Replace “incomplete” with “indefinite” and “complete” with “definite” in +[basic.lval]p7, so that the target of a pointer can be modifiable if it has +sizeless definite type. + +4.1 Lvalue-to-rvalue conversion [conv.lval] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “indefinite” in [conv.lval]p1, so that sizeless +definite glvalues can be converted to prvalues. + +5.2.2 Function call [expr.call] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “completely-defined” with “definite” and “incomplete class type” with +“indefinite type” in [expr.call]p4, so that parameters can have sizeless +definite type. + +Replace “incomplete” with “indefinite” and “complete” with “definite” in +[expr.call]p11, so that function call prvalues can have sizeless definite type. + +5.2.3 Explicit type conversion (function notation) [expr.type.conv] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” in [expr.type.conv]p2, so that ``T()`` +can be used for sizeless definite T. + +5.3.1 Unary operators [expr.unary.op] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “indefinite” in [expr.unary.op]p1, so that a +dereferenced pointer to a sizeless definite object can be converted to +a prvalue. + +5.3.5 Delete [expr.delete] +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After the first sentence in [expr.delete]p2 (which describes converting an +operand with class type to a pointer type), add: + + The type of the operand must now be a pointer to a sized type, + otherwise the program is ill-formed. + +7.1.6.2 Simple type specifiers [dcl.type.simple] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” in [dcl.type.simple]p5, so that the special +treatment for decltypes of function calls applies to indefinite rather +than incomplete return types. This is for consistency with the change +to [expr.call]p11 above. + +8.3.4 Arrays [dcl.array] +~~~~~~~~~~~~~~~~~~~~~~~~ + +In [dcl.array]p1, add “a sizeless type” to the list of things that array +element type T cannot be. + +9.4.2 Static data members [class.static.data] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “an incomplete type” with “a sized indefinite type” in +[class.static.data]p2, to avoid giving the impression that static data +members can have sizeless type. + +Make this explicit by adding the following after [class.static.data]p7: + +* A static data member shall not have sizeless type. + +14.3.1 Template type parameters [temp.arg.type] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “indefinite” in [temp.arg.type]p2, which notes that +template type parameters need not be fully defined. + +14.7.1 Implicit instantiation [temp.inst] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “completely-defined object type” with “definite object type” +in [temp.inst]p1 and [temp.inst]p6, so that the language edits do not affect +the rules for implicit instantiation. + +17.6.4.8 Other functions [res.on.functions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “incomplete” with “incomplete or indefinite” in [res.on.functions]p2, +so that the library requires the rest of the program to honor the rules +for both categories of type. + +20.10.4.3 Type properties [meta.unary.prop] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” in [meta.unary.prop]p3 and in the table +that follows. This specifically includes ``is_destructible``; since sizeless +definite types can have automatic storage duration, it must be possible +to destroy them. The changes are redundant but harmless for cases in +which the completeness rule applies only to class types. + +20.10.6 Relationships between types [meta.rel] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” in table 51. + +20.10.7.6 Other transformations [meta.trans.other] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Replace “complete” with “definite” in table 57. + +Notes for Clang developers +========================== + +Applying the extension to other cases +------------------------------------- + +The summary and standard edits above describe how the sizeless type +extension interacts with the core parts of the C and C++ standards. +However, Clang supports many other extensions to the core languages, +and will support new versions of the core languages as they evolve +over time. It is therefore necessary to describe how sizeless types +should interact with future extensions and language developments. + +The general principle is that we should continue to keep using the +distinction between incomplete types and complete types unless there is +a specific known benefit to doing otherwise. Treating sizeless types as +incomplete types should be the conservatively correct choice in almost +all cases. We can later decide to relax specific rules to use the +distinction between indefinite and definite types once we are sure +that that is the right thing to do. + +Note that no decision needs to be made for any rules that are specific +to complete or incomplete aggregates (arrays, structs, unions or classes), +since aggregates are always sized. + +Rationale for this extension +============================ + +Requirements +------------ + +The main question that prompted this extension was: how do we add +scalable vector types to the type system? The key requirements were: + +* The approach must work in both C and C++. + +* It must be possible to define automatic variables with these types. + +* It must be possible to pass and return objects of these types + (since that is what intrinsics and vector library routines need to do). + +* It must be possible to use the types in ``_Generic`` associations + (since the SVE ACLE uses ``_Generic`` to provide ``tgmath.h``\ -style + overloads). + +* It must be possible to create pointers or references to the types + (for passing or returning by pointer or reference, and because not + allowing references would be semantically difficult in C++). + +Possible approaches +------------------- + +Any approach to defining scalable types would fall into one of three +categories: + +(1) Limit the types in such a way that there is no concept of size. + +(2) Define the size of the types to be variable. + +(3) Define the size of the types to be constant, either with the + constant being large enough for all possible vector lengths or + with the types pointing to separate memory (as for C++ classes + like ``std::string``). + +\ (2) seemed initially appealing since C already has the concept of +variable-length arrays. However, variable-length built-in types +would work in a significantly different way. Arrays often decay to +pointers (which of course are fixed-length types), whereas vector +types never would. Unlike arrays, it should be possible to pass +variable-length vectors to functions, return them from functions, +and assign them by value. + +One particular difficulty is that the semantics of variable-length arrays +rely on having a point at which the array size is evaluated. It would +be difficult to extend this approach to built-in types, or to declarations +of functions that return variable-length types. It would also not be an +accurate model of how an implementation actually behaves, since the +implementation would not evaluate the vector lengths at these points and +would not react to the results of the calculation. + +As well as the extension itself being relatively complex (especially +for C++), it might be difficult to define it in a way that interacts +naturally with other extensions. Also, variable-length arrays were added +to an early draft of C++14, but were later removed as too controversial and +did not make it into the final standard. C++17 still requires ``sizeof`` +to be constant and C11 makes variable-length arrays optional. + +\ (2) therefore felt like a complicated dead-end. + +\ (3) can be divided into two parts: + +a) The vector types have a constant size and are large enough for all + possible vector lengths. + + The main problem with this approach is that the maximum SVE vector + length of 2048 bits is much larger than the minimum of 128 bits. Using + a fixed size of 2048 bits would be extremely inefficient for smaller + vector lengths, and of course the whole point of using vectors is to + make things *more* efficient. + + Also, we would need to define the types such that only the bytes + associated with the actual vector length are significant. This would + make it possible to pass or return the types in registers and treat + them as register values when copying. This perhaps has some similarity + with overaligned structures such as: + + .. code-block:: c + + struct s { _Alignas(16) int i; }; + + except that the amount of padding is only known at runtime. + + There is also a significant conceptual problem: encoding a fixed size + goes against the guiding principle of SVE, in which there is no preferred + vector length. There is nothing particularly magical about the current + limit of 2048 bits and it would be better to avoid an ABI break if the + maximum ever did increase in future. + +b) The vector types have a constant size and refer to separate storage + (as for C++ classes like ``std::string``). + + This would be difficult to do without C++-style constructor, destructor, + copy and move semantics, so would not work well in C. And in C++ it would + be less efficient than the other approaches, since presumably an allocator + would be needed to allocate the separate storage. It would be difficult + to map this kind of type to a self-contained register-based ABI type. + +These are all negative reasons for (1) being the best approach. +A more positive justification is that (1) seems to meet the requirements +in the most efficient way possible. The vectors can use their natural +(native) representation, and the type system prevents uses that would +make that representation problematic. + +Also, the approach of starting with very restricted types and then +specifically allowing certain things should be more future-proof +and interact better with other (unseen) language extensions. By default, +any language extension would treat the new types like other incomplete +types and choose conservatively-correct behavior. It would then be +possible to relax the rules if this default behavior turns out to be +too restrictive. + +(That said, treating the types as permanently incomplete will +not avoid all clashes with other extensions. For example, we need to +allow objects of automatic storage duration to have certain forms of +incomplete type, whereas an extension might implicitly assume that all +such objects must already have complete type. The approach should still +avoid the worst effects though.) Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -282,7 +282,7 @@ MLV_DuplicateVectorComponents, MLV_InvalidExpression, MLV_LValueCast, // Specialized form of MLV_InvalidExpression. - MLV_IncompleteType, + MLV_IndefiniteType, MLV_ConstQualified, MLV_ConstQualifiedField, MLV_ConstAddrSpace, @@ -337,7 +337,7 @@ CM_ConstQualifiedField, CM_ConstAddrSpace, CM_ArrayType, - CM_IncompleteType + CM_IndefiniteType }; private: Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2547,6 +2547,8 @@ "redeclaration has different alignment requirement (%1 vs %0)">; def err_alignas_underaligned : Error< "requested alignment is less than minimum alignment of %1 for type %0">; +def err_attribute_sizeless_type : Error< + "%0 attribute cannot be applied to sizeless type %1">; def err_attribute_argument_n_type : Error< "%0 attribute requires parameter %1 to be %select{int or bool|an integer " "constant|a string|an identifier}2">; @@ -6432,6 +6434,8 @@ "implicit conversion from array size expression of type %0 to " "%select{integral|enumeration}1 type %2 is a C++11 extension">, InGroup; +def err_array_of_sizeless : Error< + "array has sizeless element type %0">; def warn_cxx98_compat_array_size_conversion : Warning< "implicit conversion from array size expression of type %0 to " "%select{integral|enumeration}1 type %2 is incompatible with C++98">, @@ -8339,6 +8343,8 @@ "__block attribute not allowed, only allowed on local variables">; def err_block_on_vm : Error< "__block attribute not allowed on declaration with a variably modified type">; +def err_sizeless_nonlocal : Error< + "non-local variable with sizeless type %0">; def err_vec_builtin_non_vector : Error< "first two arguments to %0 must be vectors">; Index: include/clang/Sema/Initialization.h =================================================================== --- include/clang/Sema/Initialization.h +++ include/clang/Sema/Initialization.h @@ -1044,8 +1044,8 @@ /// Default-initialization of a 'const' object. FK_DefaultInitOfConst, - /// Initialization of an incomplete type. - FK_Incomplete, + /// Initialization of an indefinite type. + FK_Indefinite, /// Variable-length array must not have an initializer. FK_VariableLengthArrayHasInitializer, @@ -1075,8 +1075,8 @@ /// The candidate set created when initialization failed. OverloadCandidateSet FailedCandidateSet; - /// The incomplete type that caused a failure. - QualType FailedIncompleteType; + /// The indefinite type that caused a failure. + QualType FailedIndefiniteType; /// The fixit that needs to be applied to make this initialization /// succeed. @@ -1346,8 +1346,8 @@ void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; this->Failure = Failure; - assert((Failure != FK_Incomplete || !FailedIncompleteType.isNull()) && - "Incomplete type failure requires a type!"); + assert((Failure != FK_Indefinite || !FailedIndefiniteType.isNull()) && + "Indefinite type failure requires a type!"); } /// Note that this initialization sequence failed due to failed @@ -1367,10 +1367,10 @@ } /// Note that this initialization sequence failed due to an - /// incomplete type. - void setIncompleteTypeFailure(QualType IncompleteType) { - FailedIncompleteType = IncompleteType; - SetFailed(FK_Incomplete); + /// indefinite type. + void setIndefiniteTypeFailure(QualType IndefiniteType) { + FailedIndefiniteType = IndefiniteType; + SetFailed(FK_Indefinite); } /// Determine why initialization failed. Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1628,7 +1628,8 @@ void CheckAddressOfNoDeref(const Expr *E); void CheckMemberAccessOfNoDeref(const MemberExpr *E); - bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, + bool RequireDefiniteTypeImpl(SourceLocation Loc, QualType T, + bool AllowSizeless, TypeDiagnoser *Diagnoser); struct ModuleScope { @@ -1718,14 +1719,33 @@ bool isUsualDeallocationFunction(const CXXMethodDecl *FD); + bool isDefiniteType(SourceLocation Loc, QualType T) { + return !RequireDefiniteTypeImpl(Loc, T, true, nullptr); + } + bool isIndefiniteType(SourceLocation Loc, QualType T) { + return !isDefiniteType(Loc, T); + } bool isCompleteType(SourceLocation Loc, QualType T) { - return !RequireCompleteTypeImpl(Loc, T, nullptr); + return !RequireDefiniteTypeImpl(Loc, T, false, nullptr); } - bool RequireCompleteType(SourceLocation Loc, QualType T, + bool RequireDefiniteType(SourceLocation Loc, QualType T, bool AllowSizeless, TypeDiagnoser &Diagnoser); - bool RequireCompleteType(SourceLocation Loc, QualType T, + bool RequireDefiniteType(SourceLocation Loc, QualType T, bool AllowSizeless, unsigned DiagID); - + template + bool RequireDefiniteType(SourceLocation Loc, QualType T, bool AllowSizeless, + unsigned DiagID, const Ts &...Args) { + BoundTypeDiagnoser Diagnoser(DiagID, Args...); + return RequireDefiniteType(Loc, T, AllowSizeless, Diagnoser); + } + bool RequireCompleteType(SourceLocation Loc, QualType T, + TypeDiagnoser &Diagnoser) { + return RequireDefiniteType(Loc, T, false, Diagnoser); + } + bool RequireCompleteType(SourceLocation Loc, QualType T, + unsigned DiagID) { + return RequireDefiniteType(Loc, T, false, DiagID); + } template bool RequireCompleteType(SourceLocation Loc, QualType T, unsigned DiagID, const Ts &...Args) { Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -2475,6 +2475,10 @@ StructSize.getValue() == static_cast(getTypeSize(Ty)); } + // Sizeless built-in types have a unique representation. + if (Ty->isSizelessBuiltinType()) + return true; + // FIXME: More cases to handle here (list by rsmith): // vectors (careful about, eg, vector of 3 foo) // _Complex int and friends Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -637,9 +637,11 @@ // Arrays are not modifiable, only their elements are. if (CT->isArrayType()) return Cl::CM_ArrayType; - // Incomplete types are not modifiable. - if (CT->isIncompleteType()) - return Cl::CM_IncompleteType; + // Incomplete types are not modifiable (C11 6.3.2.1p1). The sizeless type + // extension replaces "incomplete" with "indefinite", so that sizeless + // definite types are modifiable lvalues. + if (CT->isIndefiniteType()) + return Cl::CM_IndefiniteType; // Records with any const fields (recursively) are not modifiable. if (const RecordType *R = CT->getAs()) @@ -701,7 +703,7 @@ case Cl::CM_ConstQualifiedField: return MLV_ConstQualifiedField; case Cl::CM_ConstAddrSpace: return MLV_ConstAddrSpace; case Cl::CM_ArrayType: return MLV_ArrayType; - case Cl::CM_IncompleteType: return MLV_IncompleteType; + case Cl::CM_IndefiniteType: return MLV_IndefiniteType; } llvm_unreachable("Unhandled modifiable type"); } Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2159,16 +2159,18 @@ } bool QualType::isCXX98PODType(const ASTContext &Context) const { - // The compiler shouldn't query this for incomplete types, but the user might. + // The compiler shouldn't query this for indefinite types, but the user might. // We return false for that case. Except for incomplete arrays of PODs, which // are PODs according to the standard. + // + // Sizeless built-in types (like other built-in types) are POD. if (isNull()) return false; if ((*this)->isIncompleteArrayType()) return Context.getBaseElementType(*this).isCXX98PODType(Context); - if ((*this)->isIncompleteType()) + if ((*this)->isIndefiniteType()) return false; if (hasNonTrivialObjCLifetime()) @@ -2216,9 +2218,10 @@ if ((*this)->isArrayType()) return Context.getBaseElementType(*this).isTrivialType(Context); - // Return false for incomplete types after skipping any incomplete array + // Return false for indefinite types after skipping any incomplete array // types which are expressly allowed by the standard and thus our API. - if ((*this)->isIncompleteType()) + // Fall through to the code below for (incomplete) sizeless definite types. + if ((*this)->isIndefiniteType()) return false; if (hasNonTrivialObjCLifetime()) @@ -2234,7 +2237,11 @@ // types. // As an extension, Clang treats vector types as Scalar types. - if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) + // Built-in sizeless types are an extension to the standard and they + // are all trivial. + if (CanonicalType->isScalarType() || + CanonicalType->isSizelessBuiltinType() || + CanonicalType->isVectorType()) return true; if (const auto *RT = CanonicalType->getAs()) { if (const auto *ClassDecl = dyn_cast(RT->getDecl())) { @@ -2270,13 +2277,18 @@ if (CanonicalType->isDependentType()) return false; - // Return false for incomplete types after skipping any incomplete array types + // Return false for indefinite types after skipping any incomplete array types // which are expressly allowed by the standard and thus our API. - if (CanonicalType->isIncompleteType()) + // Fall through to the code below for (incomplete) sizeless definite types. + if (CanonicalType->isIndefiniteType()) return false; // As an extension, Clang treats vector types as Scalar types. - if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) + // Built-in sizeless types are an extension to the standard and they + // are all trivially copyable. + if (CanonicalType->isScalarType() || + CanonicalType->isSizelessBuiltinType() || + CanonicalType->isVectorType()) return true; if (const auto *RT = CanonicalType->getAs()) { @@ -2507,13 +2519,19 @@ const Type *BaseTy = ty->getBaseElementTypeUnsafe(); assert(BaseTy && "NULL element type"); - // Return false for incomplete types after skipping any incomplete array + // Return false for indefinite types after skipping any incomplete array // types which are expressly allowed by the standard and thus our API. - if (BaseTy->isIncompleteType()) + // Fall through to the code below for (incomplete) sizeless definite types. + if (BaseTy->isIndefiniteType()) return false; // As an extension, Clang treats vector types as Scalar types. - if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; + // Built-in sizeless types are an extension to the standard and they + // are all POD. + if (BaseTy->isScalarType() || + BaseTy->isSizelessBuiltinType() || + BaseTy->isVectorType()) + return true; if (const auto *RT = BaseTy->getAs()) { if (const auto *ClassDecl = dyn_cast(RT->getDecl())) { // C++11 [class]p10: Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -12505,9 +12505,12 @@ // function declarator that is part of a function definition of // that function shall not have incomplete type. // + // The sizeless type extension replaces "incomplete" with "indefinite", + // so that (incomplete) sizeless definite types can be passed to functions. + // // This is also C++ [dcl.fct]p6. if (!Param->isInvalidDecl() && - RequireCompleteType(Param->getLocation(), Param->getType(), + RequireDefiniteType(Param->getLocation(), Param->getType(), true, diag::err_typecheck_decl_incomplete_type)) { Param->setInvalidDecl(); HasInvalidParm = true; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1705,7 +1705,7 @@ if (!isa(D) || isa(D) || isa(D)) return false; - // Types of valid local variables should be complete, so this should succeed. + // Types of valid local variables should be definite, so this should succeed. if (const VarDecl *VD = dyn_cast(D)) { // White-list anything with an __attribute__((unused)) type. @@ -1717,9 +1717,9 @@ return false; } - // If we failed to complete the type for some reason, or if the type is - // dependent, don't diagnose the variable. - if (Ty->isIncompleteType() || Ty->isDependentType()) + // If we failed to make the type definite for some reason, or if the type + // is dependent, don't diagnose the variable. + if (Ty->isIndefiniteType() || Ty->isDependentType()) return false; // Look at the element type to ensure that the warning behaviour is @@ -7565,6 +7565,12 @@ return; } + if (!NewVD->hasLocalStorage() && T->isSizelessType()) { + Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T; + NewVD->setInvalidDecl(); + return; + } + if (isVM && NewVD->hasAttr()) { Diag(NewVD->getLocation(), diag::err_block_on_vm); NewVD->setInvalidDecl(); @@ -10309,7 +10315,9 @@ // But, issue any diagnostic on the first declaration only. if (Previous.empty() && NewFD->isExternC()) { QualType R = NewFD->getReturnType(); - if (R->isIncompleteType() && !R->isVoidType()) + // "Indefinite" rather than "incomplete" because C functions can + // return (incomplete) sizeless definite types. + if (R->isIndefiniteType() && !R->isVoidType()) Diag(NewFD->getLocation(), diag::warn_return_value_udt_incomplete) << NewFD << R; else if (!R.isPODType(Context) && !R->isVoidType() && @@ -11121,7 +11129,10 @@ QualType BaseDeclType = VDecl->getType(); if (const ArrayType *Array = Context.getAsIncompleteArrayType(BaseDeclType)) BaseDeclType = Array->getElementType(); - if (RequireCompleteType(VDecl->getLocation(), BaseDeclType, + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.7.9p3, to allow initialization of + // identifiers with sizeless definite type. + if (RequireDefiniteType(VDecl->getLocation(), BaseDeclType, true, diag::err_typecheck_decl_incomplete_type)) { RealDecl->setInvalidDecl(); return; @@ -11512,9 +11523,11 @@ QualType Ty = VD->getType(); if (Ty->isDependentType()) return; - // Require a complete type. - if (RequireCompleteType(VD->getLocation(), - Context.getBaseElementType(Ty), + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.7.9p3, to allow initialization of + // identifiers with sizeless definite type. + if (RequireDefiniteType(VD->getLocation(), + Context.getBaseElementType(Ty), true, diag::err_typecheck_decl_incomplete_type)) { VD->setInvalidDecl(); return; @@ -11603,9 +11616,13 @@ // Block scope. C99 6.7p7: If an identifier for an object is // declared with no linkage (C99 6.2.2p6), the type for the // object shall be complete. + // + // In practice this if statement appears to be dead, since no such + // variable would be classified as DeclarationOnly. Logically it + // should include (incomplete) sizeless definite types though. if (!Type->isDependentType() && Var->isLocalVarDecl() && !Var->hasLinkage() && !Var->isInvalidDecl() && - RequireCompleteType(Var->getLocation(), Type, + RequireDefiniteType(Var->getLocation(), Type, true, diag::err_typecheck_decl_incomplete_type)) Var->setInvalidDecl(); @@ -11686,8 +11703,10 @@ return; if (!Var->hasAttr()) { - if (RequireCompleteType(Var->getLocation(), - Context.getBaseElementType(Type), + // The sizeless type extension replaces "complete" with "definite" + // in C11 6.7p7, to allow declarations with sizeless definite type. + if (RequireDefiniteType(Var->getLocation(), + Context.getBaseElementType(Type), true, diag::err_typecheck_decl_incomplete_type)) { Var->setInvalidDecl(); return; @@ -13030,11 +13049,13 @@ } // The return type of a function definition must be complete - // (C99 6.9.1p3, C++ [dcl.fct]p6). + // (C99 6.9.1p3, C++ [dcl.fct]p6). The sizeless type extension replaces + // "complete object type" with "definite object type", so that functions + // can return sizeless definite types. QualType ResultType = FD->getReturnType(); if (!ResultType->isDependentType() && !ResultType->isVoidType() && !FD->isInvalidDecl() && - RequireCompleteType(FD->getLocation(), ResultType, + RequireDefiniteType(FD->getLocation(), ResultType, true, diag::err_func_def_incomplete_result)) FD->setInvalidDecl(); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -3899,7 +3899,9 @@ if (const auto *ED = dyn_cast(D)) UnderlyingTy = ED->getIntegerType(); } - if (DiagTy->isDependentType() || DiagTy->isIncompleteType()) + // "Indefinite" rather than "incomplete" because we want to raise an + // error for (incomplete) sizeless definite types below. + if (DiagTy->isDependentType() || DiagTy->isIndefiniteType()) return; // C++11 [dcl.align]p5, C11 6.7.5/4: @@ -3907,6 +3909,7 @@ // not specify an alignment that is less strict than the alignment that // would otherwise be required for the entity being declared. AlignedAttr *AlignasAttr = nullptr; + AlignedAttr *LastAlignedAttr = nullptr; unsigned Align = 0; for (auto *I : D->specific_attrs()) { if (I->isAlignmentDependent()) @@ -3914,9 +3917,13 @@ if (I->isAlignas()) AlignasAttr = I; Align = std::max(Align, I->getAlignment(Context)); + LastAlignedAttr = I; } - if (AlignasAttr && Align) { + if (Align && DiagTy->isSizelessType()) { + Diag(LastAlignedAttr->getLocation(), diag::err_attribute_sizeless_type) + << LastAlignedAttr->getSpelling() << DiagTy; + } else if (AlignasAttr && Align) { CharUnits RequestedAlign = Context.toCharUnitsFromBits(Align); CharUnits NaturalAlign = Context.getTypeAlignInChars(UnderlyingTy); if (NaturalAlign > RequestedAlign) Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -247,7 +247,9 @@ bool Sema::SetParamDefaultArgument(ParmVarDecl *Param, Expr *Arg, SourceLocation EqualLoc) { - if (RequireCompleteType(Param->getLocation(), Param->getType(), + // The sizeless type extension allows parameters to have (incomplete) + // sizeless definite type. + if (RequireDefiniteType(Param->getLocation(), Param->getType(), true, diag::err_typecheck_decl_incomplete_type)) { Param->setInvalidDecl(); return true; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -783,7 +783,11 @@ /// Incomplete types are considered POD, since this check can be performed /// when we're in an unevaluated context. Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { - if (Ty->isIncompleteType()) { + // "Indefinite" rather than "incomplete" since (incomplete) sizeless + // definite types can be passed to functions. In practice the choice + // makes no difference here, since all sizeless types are POD and so + // are valid via that route. + if (Ty->isIndefiniteType()) { // C++11 [expr.call]p7: // After these conversions, if the argument does not have arithmetic, // enumeration, pointer, pointer to member, or class type, the program @@ -929,8 +933,11 @@ return Comma.get(); } + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.5.2.2p4, so that arguments can + // have sizeless definite type. if (!getLangOpts().CPlusPlus && - RequireCompleteType(E->getExprLoc(), E->getType(), + RequireDefiniteType(E->getExprLoc(), E->getType(), true, diag::err_call_incomplete_argument)) return ExprError(); @@ -1483,8 +1490,11 @@ } else { // C11 6.5.1.1p2 "The type name in a generic association shall specify a // complete object type other than a variably modified type." + // + // The sizeless type extension replaces "complete" with "definite", + // so that _Generic can be used with sizeless definite types. unsigned D = 0; - if (Types[i]->getType()->isIncompleteType()) + if (Types[i]->getType()->isIndefiniteType()) D = diag::err_assoc_type_incomplete; else if (!Types[i]->getType()->isObjectType()) D = diag::err_assoc_type_nonobject; @@ -5069,7 +5079,10 @@ if (ArgIx < Args.size()) { Arg = Args[ArgIx++]; - if (RequireCompleteType(Arg->getBeginLoc(), ProtoArgType, + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.5.2.2p4, so that arguments can + // have sizeless definite type. + if (RequireDefiniteType(Arg->getBeginLoc(), ProtoArgType, true, diag::err_call_incomplete_argument, Arg)) return true; @@ -5901,7 +5914,10 @@ Arg = ArgE.getAs(); } - if (RequireCompleteType(Arg->getBeginLoc(), Arg->getType(), + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.5.2.2p4, so that arguments can + // have sizeless definite type. + if (RequireDefiniteType(Arg->getBeginLoc(), Arg->getType(), true, diag::err_call_incomplete_argument, Arg)) return ExprError(); @@ -5967,7 +5983,10 @@ return ExprError(Diag(LParenLoc, diag::err_variable_object_no_init) << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd())); } else if (!literalType->isDependentType() && - RequireCompleteType(LParenLoc, literalType, + // The sizeless type extension replaces "complete object type" + // with "definite object type" in C11 6.5.2.5p1, so that compound + // literals can have sizeless definite type. + RequireDefiniteType(LParenLoc, literalType, true, diag::err_typecheck_decl_incomplete_type, SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) return ExprError(); @@ -7252,6 +7271,10 @@ /*isIntFirstExpr=*/false)) return LHSTy; + // Allow ?: operations in which both operands have the same sizeless type. + if (LHSTy->isSizelessType() && LHSTy == RHSTy) + return LHSTy; + // Emit a better diagnostic if one of the expressions is a null pointer // constant and the other is not a pointer type. In this case, the user most // likely forgot to take the address of the other expression. @@ -11408,7 +11431,7 @@ case Expr::MLV_ClassTemporary: DiagID = diag::err_typecheck_expression_not_modifiable_lvalue; break; - case Expr::MLV_IncompleteType: + case Expr::MLV_IndefiniteType: case Expr::MLV_IncompleteVoidType: return S.RequireCompleteType(Loc, E->getType(), diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); @@ -11692,7 +11715,10 @@ if (RHS.isInvalid()) return QualType(); if (!RHS.get()->getType()->isVoidType()) - S.RequireCompleteType(Loc, RHS.get()->getType(), + // The test is for definiteness rather than completeness because + // the sizeless type extension allows (incomplete) sizeless definite + // types on the rhs of a comma operator. + S.RequireDefiniteType(Loc, RHS.get()->getType(), true, diag::err_incomplete_type); } @@ -14010,7 +14036,11 @@ << OrigExpr->getType() << E->getSourceRange()); if (!TInfo->getType()->isDependentType()) { - if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), + // The test is for definiteness rather than completeness because the + // sizeless type extension allows (incomplete) sizeless definite types + // to be passed to functions. + if (RequireDefiniteType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), true, diag::err_second_parameter_to_va_arg_incomplete, TInfo->getTypeLoc())) return ExprError(); @@ -16240,7 +16270,10 @@ bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, CallExpr *CE, FunctionDecl *FD) { - if (ReturnType->isVoidType() || !ReturnType->isIncompleteType()) + // The sizeless type extension replaces "complete object type" with + // "definite object type" in C11 6.5.2.2p1, so that functions can + // return sizeless definite types. + if (ReturnType->isVoidType() || ReturnType->isDefiniteType()) return false; // If we're inside a decltype's expression, don't check for a valid return @@ -16273,7 +16306,7 @@ } } Diagnoser(FD, CE); - if (RequireCompleteType(Loc, ReturnType, Diagnoser)) + if (RequireDefiniteType(Loc, ReturnType, true, Diagnoser)) return true; return false; Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -1418,8 +1418,11 @@ // C++17 [expr.type.conv]p2: // If the type is cv void and the initializer is (), the expression is a // prvalue of the specified type that performs no initialization. + // + // The sizeless type extension replaces "complete" with "definite", + // so that sizeless definite types can be zero-initialized using (). if (!Ty->isVoidType() && - RequireCompleteType(TyBeginLoc, ElemTy, + RequireDefiniteType(TyBeginLoc, ElemTy, true, diag::err_invalid_incomplete_type_use, FullRange)) return ExprError(); @@ -3339,7 +3342,8 @@ // this, so we treat it as a warning unless we're in a SFINAE context. Diag(StartLoc, diag::ext_delete_void_ptr_operand) << Type << Ex.get()->getSourceRange(); - } else if (Pointee->isFunctionType() || Pointee->isVoidType()) { + } else if (Pointee->isFunctionType() || Pointee->isVoidType() || + Pointee->isSizelessType()) { return ExprError(Diag(StartLoc, diag::err_delete_operand) << Type << Ex.get()->getSourceRange()); } else if (!Pointee->isDependentType()) { @@ -4441,6 +4445,9 @@ // C++1z [meta.unary.prop]: // remove_all_extents_t shall be a complete type or cv void. + // + // The sizeless type extension replaces "complete" with "definite", + // so that it's possible to apply traits to sizeless definite types. case UTT_IsAggregate: case UTT_IsTrivial: case UTT_IsTriviallyCopyable: @@ -4473,8 +4480,8 @@ if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType()) return true; - return !S.RequireCompleteType( - Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr); + return !S.RequireDefiniteType( + Loc, ArgTy, true, diag::err_incomplete_type_used_in_type_trait_expr); } } @@ -4726,7 +4733,11 @@ // C++14 [meta.unary.prop]: // For incomplete types and function types, is_destructible::value is // false. - if (T->isIncompleteType() || T->isFunctionType()) + // + // The sizeless type extension replaces "incomplete" with "indefinite", + // since sizeless definite types can be created with automatic storage + // duration and must therefore be destructible. + if (T->isIndefiniteType() || T->isFunctionType()) return false; // A type that requires destruction (via a non-trivial destructor or ARC @@ -4952,19 +4963,25 @@ // Precondition: T and all types in the parameter pack Args shall be // complete types, (possibly cv-qualified) void, or arrays of // unknown bound. + // + // The sizeless type extension replaces "complete" with "definite", + // so that sizeless definite types can be used with the traits. for (const auto *TSI : Args) { QualType ArgTy = TSI->getType(); if (ArgTy->isVoidType() || ArgTy->isIncompleteArrayType()) continue; - if (S.RequireCompleteType(KWLoc, ArgTy, + if (S.RequireDefiniteType(KWLoc, ArgTy, true, diag::err_incomplete_type_used_in_type_trait_expr)) return false; } // Make sure the first argument is not incomplete nor a function type. + // + // The sizeless type extension replaces "incomplete" with "indefinite", + // so that sizeless definite types can be used with the traits. QualType T = Args[0]->getType(); - if (T->isIncompleteType() || T->isFunctionType()) + if (T->isIndefiniteType() || T->isFunctionType()) return false; // Make sure the first argument is not an abstract type. @@ -5181,7 +5198,10 @@ return LhsT->isVoidType(); // A function definition requires a complete, non-abstract return type. - if (!Self.isCompleteType(KeyLoc, RhsT) || Self.isAbstractType(KeyLoc, RhsT)) + // + // The sizeless type extension replaces "complete" with "definite", + // so that functions can return sizeless definite types. + if (!Self.isDefiniteType(KeyLoc, RhsT) || Self.isAbstractType(KeyLoc, RhsT)) return false; // Compute the result of add_rvalue_reference. @@ -5224,12 +5244,15 @@ // // For both, T and U shall be complete types, (possibly cv-qualified) // void, or arrays of unknown bound. + // + // The sizeless type extension replaces "complete" with "definite", + // so that functions can return sizeless definite types. if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() && - Self.RequireCompleteType(KeyLoc, LhsT, + Self.RequireDefiniteType(KeyLoc, LhsT, true, diag::err_incomplete_type_used_in_type_trait_expr)) return false; if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() && - Self.RequireCompleteType(KeyLoc, RhsT, + Self.RequireDefiniteType(KeyLoc, RhsT, true, diag::err_incomplete_type_used_in_type_trait_expr)) return false; @@ -7391,7 +7414,9 @@ E = Res.get(); if (!E->getType()->isVoidType()) - RequireCompleteType(E->getExprLoc(), E->getType(), + // The test is based on definiteness rather than completeness because + // (incomplete) sizeless definite types can be used in an ignored result. + RequireDefiniteType(E->getExprLoc(), E->getType(), true, diag::err_incomplete_type); return E; } Index: lib/Sema/SemaFixItUtils.cpp =================================================================== --- lib/Sema/SemaFixItUtils.cpp +++ lib/Sema/SemaFixItUtils.cpp @@ -198,6 +198,9 @@ std::string Sema::getFixItZeroInitializerForType(QualType T, SourceLocation Loc) const { + if (T->isSizelessBuiltinType() && LangOpts.CPlusPlus) + return std::string(" = ") + T.getAsString() + "()"; + if (T->isScalarType()) { std::string s = getScalarZeroExpressionForType(*T, Loc, *this); if (!s.empty()) Index: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ lib/Sema/SemaInit.cpp @@ -1114,14 +1114,18 @@ // Special-case SemaRef.Diag(IList->getInit(Index)->getBeginLoc(), DK) << IList->getInit(Index)->getSourceRange(); - } else if (!T->isIncompleteType()) { - // Don't complain for incomplete types, since we'll get an error + } else if (!T->isIndefiniteType()) { + // Don't complain for indefinite types, since we'll get an error // elsewhere QualType CurrentObjectType = StructuredList->getType(); int initKind = CurrentObjectType->isArrayType()? 0 : CurrentObjectType->isVectorType()? 1 : - CurrentObjectType->isScalarType()? 2 : + CurrentObjectType->isScalarType() || + // Treat sizeless builtin types as scalars for the purposes of + // this diagnostic, since the rules are the same. Calling them + // out as a special case is unlikely to be helpful. + CurrentObjectType->isSizelessBuiltinType()? 2 : CurrentObjectType->isUnionType()? 3 : 4; @@ -1179,7 +1183,7 @@ // parts. CheckComplexType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); - } else if (DeclType->isScalarType()) { + } else if (DeclType->isScalarType() || DeclType->isSizelessBuiltinType()) { CheckScalarType(Entity, IList, DeclType, Index, StructuredList, StructuredIndex); } else if (DeclType->isVectorType()) { @@ -3353,7 +3357,7 @@ case FK_ReferenceBindingToInitList: case FK_InitListBadDestinationType: case FK_DefaultInitOfConst: - case FK_Incomplete: + case FK_Indefinite: case FK_ArrayTypeMismatch: case FK_NonConstantArrayInit: case FK_ListInitializationFailed: @@ -3693,8 +3697,10 @@ if (!S.isStdInitializerList(DestType, &E)) return false; + // "Complete" and "definite" are the same here, because + // std::initializer_list is always sized. if (!S.isCompleteType(List->getExprLoc(), E)) { - Sequence.setIncompleteTypeFailure(E); + Sequence.setIndefiniteTypeFailure(E); return true; } @@ -3870,8 +3876,11 @@ ILE ? MultiExprArg(ILE->getInits(), ILE->getNumInits()) : Args; // The type we're constructing needs to be complete. + // + // "Complete" and "definite" are the same here, since classes are + // always sized. if (!S.isCompleteType(Kind.getLocation(), DestType)) { - Sequence.setIncompleteTypeFailure(DestType); + Sequence.setIndefiniteTypeFailure(DestType); return; } @@ -4156,8 +4165,10 @@ } if (DestType->isRecordType() && + // "Complete" and "definite" are the same here, since classes are + // always sized. !S.isCompleteType(InitList->getBeginLoc(), DestType)) { - Sequence.setIncompleteTypeFailure(DestType); + Sequence.setIndefiniteTypeFailure(DestType); return; } @@ -6036,7 +6047,9 @@ // proper call to the copy constructor. for (unsigned I = 1, N = Constructor->getNumParams(); I != N; ++I) { ParmVarDecl *Parm = Constructor->getParamDecl(I); - if (S.RequireCompleteType(Loc, Parm->getType(), + // The sizeless type extension allows parameters to have + // (incomplete) sizeless definite type. + if (S.RequireDefiniteType(Loc, Parm->getType(), true, diag::err_call_incomplete_argument)) break; @@ -8437,8 +8450,12 @@ case OR_No_Viable_Function: { auto Cands = FailedCandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args); - if (!S.RequireCompleteType(Kind.getLocation(), - DestType.getNonReferenceType(), + // The condition is "definiteness" rather than "completeness" because + // (incomplete) sizeless definite types are never completed, and so + // completeness itself is not the problem. The usual "not viable" + // diagnostics are better in that case. + if (!S.RequireDefiniteType(Kind.getLocation(), + DestType.getNonReferenceType(), true, diag::err_typecheck_nonviable_condition_incomplete, OnlyArg->getType(), Args[0]->getSourceRange())) S.Diag(Kind.getLocation(), diag::err_typecheck_nonviable_condition) @@ -8726,9 +8743,9 @@ } break; - case FK_Incomplete: - S.RequireCompleteType(Kind.getLocation(), FailedIncompleteType, - diag::err_init_incomplete_type); + case FK_Indefinite: + S.RequireDefiniteType(Kind.getLocation(), FailedIndefiniteType, + true, diag::err_init_incomplete_type); break; case FK_ListInitializationFailed: { @@ -8891,8 +8908,8 @@ OS << "default initialization of a const variable"; break; - case FK_Incomplete: - OS << "initialization of incomplete type"; + case FK_Indefinite: + OS << "initialization of indefinite type"; break; case FK_ListInitializationFailed: Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -474,8 +474,10 @@ if (!LSI->ReturnType->isDependentType() && !LSI->ReturnType->isVoidType()) { - if (RequireCompleteType(CallOperator->getBeginLoc(), LSI->ReturnType, - diag::err_lambda_incomplete_result)) { + // The sizeless type extension allows (incomplete) sizeless + // definite types to be returned from functions. + if (RequireDefiniteType(CallOperator->getBeginLoc(), LSI->ReturnType, + true, diag::err_lambda_incomplete_result)) { // Do nothing. } } Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -4797,9 +4797,13 @@ ImplicitConversionSequence Result; Result.setBad(BadConversionSequence::no_conversion, From, ToType); - // We need a complete type for what follows. Incomplete types can never be + // We need a definite type for what follows. Indefinite types can never be // initialized from init lists. - if (!S.isCompleteType(From->getBeginLoc(), ToType)) + // + // The condition is "definiteness" rather than "completeness" because the + // sizeless type extension allows list construction of (incomplete) sizeless + // definite types. + if (!S.isDefiniteType(From->getBeginLoc(), ToType)) return Result; // Per DR1467: @@ -7028,7 +7032,10 @@ &ConversionRef, VK_RValue); QualType ConversionType = Conversion->getConversionType(); - if (!isCompleteType(From->getBeginLoc(), ConversionType)) { + // The condition is "definiteness" rather than "completeness" because the + // sizeless type extension allows user-defined conversions to (incomplete) + // sizeless definite types. + if (!isDefiniteType(From->getBeginLoc(), ConversionType)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_final_conversion; return; @@ -9826,13 +9833,17 @@ return; } - // Diagnose references or pointers to incomplete types differently, - // since it's far from impossible that the incompleteness triggered + // Diagnose references or pointers to indefinite types differently, + // since it's far from impossible that the indefiniteness triggered // the failure. + // + // The condition is "indefinite" rather than "incomplete" because + // (incomplete) sizeless definite types are never completed. The + // diagnostics below are a better fit for them. QualType TempFromTy = FromTy.getNonReferenceType(); if (const PointerType *PTy = TempFromTy->getAs()) TempFromTy = PTy->getPointeeType(); - if (TempFromTy->isIncompleteType()) { + if (TempFromTy->isIndefiniteType()) { // Emit the generic diagnostic and, optionally, add the hints to it. S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_conv_incomplete) << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second << FnDesc Index: lib/Sema/SemaPseudoObject.cpp =================================================================== --- lib/Sema/SemaPseudoObject.cpp +++ lib/Sema/SemaPseudoObject.cpp @@ -239,7 +239,7 @@ if (exp->isGLValue()) return true; QualType ty = exp->getType(); - assert(!ty->isIncompleteType()); + assert(ty->isDefiniteType()); assert(!ty->isDependentType()); if (const CXXRecordDecl *ClassDecl = ty->getAsCXXRecordDecl()) Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2795,7 +2795,9 @@ QualType VariableType = VD->getType(); - if (VariableType->isIncompleteType()) + // "Indefinite" rather than "incomplete" since ranges can yield (incomplete) + // sizeless definite types. + if (VariableType->isIndefiniteType()) return; const Expr *InitExpr = VD->getInit(); Index: lib/Sema/SemaStmtAsm.cpp =================================================================== --- lib/Sema/SemaStmtAsm.cpp +++ lib/Sema/SemaStmtAsm.cpp @@ -312,10 +312,10 @@ // Accept, even if we emitted an error diagnostic. break; } - case Expr::MLV_IncompleteType: + case Expr::MLV_IndefiniteType: case Expr::MLV_IncompleteVoidType: - if (RequireCompleteType(OutputExpr->getBeginLoc(), Exprs[i]->getType(), - diag::err_dereference_incomplete_type)) + if (RequireDefiniteType(OutputExpr->getBeginLoc(), Exprs[i]->getType(), + true, diag::err_dereference_incomplete_type)) return StmtError(); LLVM_FALLTHROUGH; default: @@ -324,6 +324,10 @@ << OutputExpr->getSourceRange()); } + // FIXME: should have a mechanism for sizeless types too. + if (OutputExpr->getType()->isSizelessType()) + continue; + unsigned Size = Context.getTypeSize(OutputExpr->getType()); if (!Context.getTargetInfo().validateOutputSize(Literal->getString(), Size)) { @@ -428,10 +432,17 @@ continue; if (!Ty->isVoidType() || !Info.allowsMemory()) - if (RequireCompleteType(InputExpr->getBeginLoc(), Exprs[i]->getType(), - diag::err_dereference_incomplete_type)) + // The test is for definiteness rather than completeness because + // the sizeless type extension allows sizeless types to be bound + // to register operands. + if (RequireDefiniteType(InputExpr->getBeginLoc(), Exprs[i]->getType(), + true, diag::err_dereference_incomplete_type)) return StmtError(); + // FIXME: should have a mechanism for sizeless types too. + if (Ty->isSizelessType()) + continue; + unsigned Size = Context.getTypeSize(Ty); if (!Context.getTargetInfo().validateInputSize(Literal->getString(), Size)) @@ -501,6 +512,7 @@ // Now that we have the right indexes go ahead and check. StringLiteral *Literal = Constraints[ConstraintIdx]; const Type *Ty = Exprs[ConstraintIdx]->getType().getTypePtr(); + // FIXME: should have a mechanism for sizeless types too. if (Ty->isDependentType() || Ty->isIncompleteType()) continue; @@ -583,6 +595,10 @@ if (Context.hasSameType(InTy, OutTy)) continue; // All types can be tied to themselves. + // FIXME: should have a mechanism for sizeless types too. + if (InTy->isSizelessType() || OutTy->isSizelessType()) + continue; + // Decide if the input and output are in the same domain (integer/ptr or // floating point. enum AsmDomain { @@ -762,6 +778,7 @@ } // Otherwise, it needs to be a complete type. + // FIXME: should have a mechanism for sizeless types too. if (RequireCompleteExprType(Result.get(), diag::err_asm_incomplete_type)) { return ExprError(); } Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -2130,6 +2130,12 @@ SourceRange Brackets, DeclarationName Entity) { SourceLocation Loc = Brackets.getBegin(); + + if (T->isSizelessType()) { + Diag(Loc, diag::err_array_of_sizeless) << T; + return QualType(); + } + if (getLangOpts().CPlusPlus) { // C++ [dcl.array]p1: // T is called the array element type; this type shall not be a reference @@ -7725,26 +7731,30 @@ return RequireCompleteExprType(E, Diagnoser); } -/// Ensure that the type T is a complete type. +/// Ensure that the type T is a definite type. /// -/// This routine checks whether the type @p T is complete in any -/// context where a complete type is required. If @p T is a complete -/// type, returns false. If @p T is a class template specialization, -/// this routine then attempts to perform class template -/// instantiation. If instantiation fails, or if @p T is incomplete -/// and cannot be completed, issues the diagnostic @p diag (giving it -/// the type @p T) and returns true. +/// This routine checks whether the type @p T is definite in any +/// context where a definite type is required. @p AllowSizeless +/// says whether sizeless types are allowed; it is false if +/// complete (i.e. definite and sized) types are required. +/// If @p T meets these conditions, returns false. If @p T is a class +/// template specialization, this routine then attempts to perform class +/// template instantiation. If instantiation fails, or if @p T is +/// indefinite and cannot be made definite, issues the diagnostic @p diag +/// (giving it the type @p T) and returns true. /// /// @param Loc The location in the source that the incomplete type /// diagnostic should refer to. /// /// @param T The type that this routine is examining for completeness. /// +/// @param AllowSizeless Whether to allow sizeless types. +/// /// @returns @c true if @p T is incomplete and a diagnostic was emitted, /// @c false otherwise. -bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, - TypeDiagnoser &Diagnoser) { - if (RequireCompleteTypeImpl(Loc, T, &Diagnoser)) +bool Sema::RequireDefiniteType(SourceLocation Loc, QualType T, + bool AllowSizeless, TypeDiagnoser &Diagnoser) { + if (RequireDefiniteTypeImpl(Loc, T, AllowSizeless, &Diagnoser)) return true; if (const TagType *Tag = T->getAs()) { if (!Tag->getDecl()->isCompleteDefinitionRequired()) { @@ -7891,11 +7901,12 @@ } } -/// The implementation of RequireCompleteType -bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T, +/// The implementation of RequireDefiniteType +bool Sema::RequireDefiniteTypeImpl(SourceLocation Loc, QualType T, + bool AllowSizeless, TypeDiagnoser *Diagnoser) { // FIXME: Add this assertion to make sure we always get instantiation points. - // assert(!Loc.isInvalid() && "Invalid location in RequireCompleteType"); + // assert(!Loc.isInvalid() && "Invalid location in RequireDefiniteType"); // FIXME: Add this assertion to help us flush out problems with // checking for dependent types and type-dependent expressions. // @@ -7920,7 +7931,7 @@ } NamedDecl *Def = nullptr; - bool Incomplete = T->isIncompleteType(&Def); + bool Indefinite = T->isIndefiniteType(&Def); // Check that any necessary explicit specializations are visible. For an // enum, we just need the declaration, so don't check this. @@ -7928,7 +7939,7 @@ checkSpecializationVisibility(Loc, Def); // If we have a complete type, we're done. - if (!Incomplete) { + if (!Indefinite && (AllowSizeless || !T->isSizelessType())) { // If we know about the definition but it is not visible, complain. NamedDecl *SuggestedDef = nullptr; if (Def && @@ -7973,8 +7984,8 @@ Source->CompleteType(IFace); // 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); + if (T->isDefiniteType() && (AllowSizeless || !T->isSizelessType())) + return RequireDefiniteTypeImpl(Loc, T, AllowSizeless, Diagnoser); } } @@ -8021,8 +8032,8 @@ // If we instantiated a definition, check that it's usable, even if // instantiation produced an error, so that repeated calls to this // function give consistent answers. - if (!T->isIncompleteType()) - return RequireCompleteTypeImpl(Loc, T, Diagnoser); + if (T->isDefiniteType() && (AllowSizeless || !T->isSizelessType())) + return RequireDefiniteTypeImpl(Loc, T, AllowSizeless, Diagnoser); } } @@ -8054,10 +8065,10 @@ return true; } -bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, - unsigned DiagID) { +bool Sema::RequireDefiniteType(SourceLocation Loc, QualType T, + bool AllowSizeless, unsigned DiagID) { BoundTypeDiagnoser<> Diagnoser(DiagID); - return RequireCompleteType(Loc, T, Diagnoser); + return RequireDefiniteType(Loc, T, AllowSizeless, Diagnoser); } /// Get diagnostic %select index for tag kind for Index: test/Sema/sizeless-1.c =================================================================== --- /dev/null +++ test/Sema/sizeless-1.c @@ -0,0 +1,264 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -Wno-comment -triple arm64-linux-gnu -target-feature +sve -std=c90 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -triple arm64-linux-gnu -target-feature +sve -std=gnu90 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -triple arm64-linux-gnu -target-feature +sve -std=c99 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -triple arm64-linux-gnu -target-feature +sve -std=gnu99 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -triple arm64-linux-gnu -target-feature +sve -std=c11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wall -W -triple arm64-linux-gnu -target-feature +sve -std=gnu11 %s + +typedef __SVInt8_t svint8_t; +typedef __SVInt16_t svint16_t; + +svint8_t global_int8; // expected-error {{non-local variable with sizeless type}} +extern svint8_t extern_int8; // expected-error {{non-local variable with sizeless type}} +static svint8_t static_int8; // expected-error {{non-local variable with sizeless type}} +svint8_t *global_int8_ptr; +extern svint8_t *extern_int8_ptr; +static svint8_t *static_int8_ptr; + +typedef svint8_t int8_typedef; +typedef svint8_t *int8_ptr_typedef; + +int sizeof_int8 = sizeof(svint8_t); // expected-error {{incomplete type}} + +int alignof_int8 = _Alignof(svint8_t); // expected-error {{incomplete type}} + +void pass_int8(svint8_t); // expected-note {{passing argument to parameter here}} + +svint8_t return_int8(); + +typedef svint8_t vec_int8_a __attribute__((vector_size(64))); // expected-error {{invalid vector element type}} +typedef svint8_t vec_int8_b __attribute__((ext_vector_type(4))); // expected-error {{invalid vector element type}} + +void dump(const volatile void *); + +void __attribute__((overloadable)) overf(svint8_t); +void __attribute__((overloadable)) overf(svint16_t); + +void __attribute__((overloadable)) overf8(svint8_t); // expected-note + {{not viable}} +void __attribute__((overloadable)) overf8(int); // expected-note + {{not viable}} + +void __attribute__((overloadable)) overf16(svint16_t); // expected-note + {{not viable}} +void __attribute__((overloadable)) overf16(int); // expected-note + {{not viable}} + +void noproto(); +void varargs(int, ...); + +void unused() { + svint8_t unused_int8; // expected-warning {{unused}} +} + +struct incomplete_struct *incomplete_ptr; + +void func(int sel) { + svint8_t local_int8; + svint16_t local_int16; + + svint8_t __attribute__((aligned)) aligned_int8_1; // expected-error {{sizeless type}} + svint8_t __attribute__((aligned(4))) aligned_int8_2; // expected-error {{sizeless type}} + svint8_t _Alignas(int) aligned_int8_3; // expected-error {{sizeless type}} + int _Alignas(svint8_t) aligned_int; // expected-error {{incomplete type}} + + // 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), ""); + _Static_assert(__atomic_is_lock_free(2, &local_int8) == __atomic_is_lock_free(2, incomplete_ptr), ""); // expected-error {{static_assert expression is not an integral constant expression}} + _Static_assert(__atomic_always_lock_free(1, &local_int8) == __atomic_always_lock_free(1, incomplete_ptr), ""); + _Static_assert(__atomic_always_lock_free(2, &local_int8) == __atomic_always_lock_free(2, incomplete_ptr), ""); + + local_int8; // expected-warning {{expression result unused}} + + (void)local_int8; + + local_int8, 0; // expected-warning + {{expression result unused}} + + 0, local_int8; // expected-warning + {{expression result unused}} + + svint8_t init_int8 = local_int8; + svint8_t bad_init_int8 = for; // expected-error {{expected expression}} + + int empty_brace_init_int = {}; // expected-error {{scalar initializer cannot be empty}} + svint8_t empty_brace_init_int8 = {}; // expected-error {{scalar initializer cannot be empty}} + svint8_t brace_init_int8 = { local_int8 }; + svint8_t bad_brace_init_int8 = { local_int8, 0 }; // expected-warning {{excess elements in scalar initializer}} + + const svint8_t const_int8 = local_int8; // expected-note {{declared const here}} + + const svint8_t uninit_const_int8; + + volatile svint8_t volatile_int8; + + const volatile svint8_t const_volatile_int8; // expected-note {{declared const here}} + + _Atomic svint8_t atomic_int8; // expected-error {{_Atomic cannot be applied to incomplete type}} + __restrict svint8_t restrict_int8; // expected-error {{requires a pointer or reference}} + + svint8_t array_int8[1]; // expected-error {{array has sizeless element type}} + + _Bool test_int8 = init_int8; // expected-error {{incompatible type}} + + int int_int8 = init_int8; // expected-error {{incompatible type}} + + init_int8 = local_int8; + init_int8 = local_int16; // expected-error {{incompatible type}} + init_int8 = sel; // expected-error {{incompatible type}} + + sel = local_int8; // expected-error {{incompatible type}} + + local_int8 = (svint8_t)local_int8; // expected-error {{cast to incomplete type}} + local_int8 = (const svint8_t)local_int8; // expected-error {{cast to incomplete type}} + local_int8 = (svint8_t)local_int8; // expected-error {{cast to incomplete type}} + + init_int8 = local_int8; + init_int8 = const_int8; + init_int8 = volatile_int8; + init_int8 = const_volatile_int8; + + const_int8 = local_int8; // expected-error {{const-qualified type}} + + volatile_int8 = local_int8; + volatile_int8 = const_int8; + volatile_int8 = volatile_int8; + volatile_int8 = const_volatile_int8; + + const_volatile_int8 = local_int8; // expected-error {{const-qualified type}} + + init_int8 = sel ? init_int8 : local_int8; + init_int8 = sel ? init_int8 : const_int8; + init_int8 = sel ? volatile_int8 : const_int8; + init_int8 = sel ? volatile_int8 : const_volatile_int8; + + pass_int8(local_int8); + pass_int8(local_int16); // expected-error {{incompatible type}} + + local_int8 = return_int8(); + local_int16 = return_int8(); // expected-error {{incompatible type}} + + dump(&local_int8); + dump(&const_int8); + dump(&volatile_int8); + dump(&const_volatile_int8); + + dump(&local_int8 + 1); // expected-error {{arithmetic on a pointer to an incomplete type}} + + *&local_int8 = local_int8; + *&const_int8 = local_int8; // expected-error {{read-only variable}} + *&volatile_int8 = local_int8; + *&const_volatile_int8 = local_int8; // expected-error {{read-only variable}} + + (&local_int8)[0] = local_int8; // expected-error {{subscript of pointer to incomplete type}} + + overf(local_int8); + overf(local_int16); + + overf8(local_int8); + overf8(local_int16); // expected-error {{no matching function}} + + overf16(local_int8); // expected-error {{no matching function}} + overf16(local_int16); + + noproto(local_int8); + varargs(1, local_int8, local_int16); + + +init_int8; // expected-error {{invalid argument type}} + ++init_int8; // expected-error {{cannot increment}} + -init_int8; // expected-error {{invalid argument type}} + --init_int8; // expected-error {{cannot decrement}} + ~init_int8; // expected-error {{invalid argument type}} + !init_int8; // expected-error {{invalid argument type}} + *init_int8; // expected-error {{requires pointer operand}} + __real init_int8; // expected-error {{invalid type}} + __imag init_int8; // expected-error {{invalid type}} + + local_int8 + init_int8; // expected-error {{invalid operands}} + local_int8 - init_int8; // expected-error {{invalid operands}} + local_int8 * init_int8; // expected-error {{invalid operands}} + local_int8 / init_int8; // expected-error {{invalid operands}} + local_int8 % init_int8; // expected-error {{invalid operands}} + local_int8 & init_int8; // expected-error {{invalid operands}} + local_int8 | init_int8; // expected-error {{invalid operands}} + local_int8 ^ init_int8; // expected-error {{invalid operands}} + local_int8 << init_int8; // expected-error {{invalid operands}} + local_int8 >> init_int8; // expected-error {{invalid operands}} + local_int8 < init_int8; // expected-error {{invalid operands}} + local_int8 <= init_int8; // expected-error {{invalid operands}} + local_int8 == init_int8; // expected-error {{invalid operands}} + local_int8 != init_int8; // expected-error {{invalid operands}} + local_int8 >= init_int8; // expected-error {{invalid operands}} + local_int8 > init_int8; // expected-error {{invalid operands}} + local_int8 && init_int8; // expected-error {{invalid operands}} + local_int8 || init_int8; // expected-error {{invalid operands}} + + local_int8 += init_int8; // expected-error {{invalid operands}} + local_int8 -= init_int8; // expected-error {{invalid operands}} + local_int8 *= init_int8; // expected-error {{invalid operands}} + local_int8 /= init_int8; // expected-error {{invalid operands}} + local_int8 %= init_int8; // expected-error {{invalid operands}} + local_int8 &= init_int8; // expected-error {{invalid operands}} + local_int8 |= init_int8; // expected-error {{invalid operands}} + local_int8 ^= init_int8; // expected-error {{invalid operands}} + local_int8 <<= init_int8; // expected-error {{invalid operands}} + local_int8 >>= init_int8; // expected-error {{invalid operands}} + + local_int8 + 0; // expected-error {{invalid operands}} + local_int8 - 0; // expected-error {{invalid operands}} + local_int8 * 0; // expected-error {{invalid operands}} + local_int8 / 0; // expected-error {{invalid operands}} + local_int8 % 0; // expected-error {{invalid operands}} + local_int8 & 0; // expected-error {{invalid operands}} + local_int8 | 0; // expected-error {{invalid operands}} + local_int8 ^ 0; // expected-error {{invalid operands}} + local_int8 << 0; // expected-error {{invalid operands}} + local_int8 >> 0; // expected-error {{invalid operands}} + local_int8 < 0; // expected-error {{invalid operands}} + local_int8 <= 0; // expected-error {{invalid operands}} + local_int8 == 0; // expected-error {{invalid operands}} + local_int8 != 0; // expected-error {{invalid operands}} + local_int8 >= 0; // expected-error {{invalid operands}} + local_int8 > 0; // expected-error {{invalid operands}} + local_int8 && 0; // expected-error {{invalid operands}} + local_int8 || 0; // expected-error {{invalid operands}} + + if (local_int8) {} // expected-error {{statement requires expression of scalar type}} + while (local_int8) {} // expected-error {{statement requires expression of scalar type}} + do {} while (local_int8); // expected-error {{statement requires expression of scalar type}} + switch (local_int8) { default:; } // expected-error {{integer type}} +} + +int vararg_receiver(int count, svint8_t first, ...) { + __builtin_va_list va; + + __builtin_va_start(va, first); + __builtin_va_arg(va, svint8_t); + __builtin_va_end(va); + return count; +} + +struct sized_struct { + int f1; + svint8_t f2; // expected-error {{field has incomplete type}} + svint8_t f3 : 2; // expected-error {{incomplete type}} +}; + +union sized_union { + int f1; + svint8_t f2; // expected-error {{field has incomplete type}} + svint8_t f3 : 2; // expected-error {{incomplete type}} +}; + +#if __STDC_VERSION__ >= 201112L +void test_generic(void) { + svint8_t local_int8; + svint16_t local_int16; + + int a1[_Generic (local_int8, svint8_t: 1, svint16_t: 2, default: 3) == 1 ? 1 : -1]; + int a2[_Generic (local_int16, svint8_t: 1, svint16_t: 2, default: 3) == 2 ? 1 : -1]; + int a3[_Generic (0, svint8_t: 1, svint16_t: 2, default: 3) == 3 ? 1 : -1]; + (void)_Generic (0, svint8_t: 1, svint8_t: 2, default: 3); // expected-error {{compatible with previously specified}} expected-note {{compatible type}} +} + +void test_compound_literal(void) { + svint8_t local_int8; + + (void) (svint8_t) { local_int8 }; +} +#endif Index: test/SemaCXX/sizeless-1.cpp =================================================================== --- /dev/null +++ test/SemaCXX/sizeless-1.cpp @@ -0,0 +1,540 @@ +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=c++98 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=gnu++98 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=c++11 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=gnu++11 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=c++14 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=gnu++14 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=c++17 %s +// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=gnu++17 %s + +namespace std { struct type_info; } + +typedef __SVInt8_t svint8_t; +typedef __SVInt16_t svint16_t; + +svint8_t global_int8; // expected-error {{non-local variable with sizeless type}} +extern svint8_t extern_int8; // expected-error {{non-local variable with sizeless type}} +static svint8_t static_int8; // expected-error {{non-local variable with sizeless type}} +svint8_t *global_int8_ptr; +extern svint8_t *extern_int8_ptr; +static svint8_t *static_int8_ptr; + +typedef svint8_t int8_typedef; +typedef svint8_t *int8_ptr_typedef; + +int sizeof_int8 = sizeof(svint8_t); // expected-error {{incomplete type}} + +#if __cplusplus >= 201103L +int alignof_int8 = alignof(svint8_t); // expected-error {{incomplete type}} +#else +int alignof_int8 = _Alignof(svint8_t); // expected-error {{incomplete type}} +#endif + +void pass_int8(svint8_t); // expected-note {{no known conversion}} + +extern "C" svint8_t return_int8(); + +typedef svint8_t vec_int8_a __attribute__((vector_size(64))); // expected-error {{invalid vector element type}} +typedef svint8_t vec_int8_b __attribute__((ext_vector_type(4))); // expected-error {{invalid vector element type}} + +void dump(const volatile void *); + +void overf(svint8_t); +void overf(svint16_t); + +void overf8(svint8_t); // expected-note + {{not viable}} +void overf8(int); // expected-note + {{not viable}} + +void overf16(svint16_t); // expected-note + {{not viable}} +void overf16(int); // expected-note + {{not viable}} + +void varargs(int, ...); + +void unused() { + svint8_t unused_int8; // expected-warning {{unused}} +} + +struct incomplete_struct *incomplete_ptr; + +void func(int sel) { + svint8_t local_int8; + svint16_t local_int16; + + svint8_t __attribute__((aligned)) aligned_int8_1; // expected-error {{sizeless type}} + svint8_t __attribute__((aligned(4))) aligned_int8_2; // expected-error {{sizeless type}} + svint8_t _Alignas(int) aligned_int8_3; // expected-error {{sizeless type}} + int _Alignas(svint8_t) aligned_int; // expected-error {{incomplete type}} + + // 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), ""); + _Static_assert(__atomic_is_lock_free(2, &local_int8) == __atomic_is_lock_free(2, incomplete_ptr), ""); // expected-error {{static_assert expression is not an integral constant expression}} + _Static_assert(__atomic_always_lock_free(1, &local_int8) == __atomic_always_lock_free(1, incomplete_ptr), ""); + _Static_assert(__atomic_always_lock_free(2, &local_int8) == __atomic_always_lock_free(2, incomplete_ptr), ""); + + local_int8; // expected-warning {{expression result unused}} + + (void)local_int8; + + local_int8, 0; // expected-warning + {{expression result unused}} + + 0, local_int8; // expected-warning + {{expression result unused}} + + svint8_t init_int8 = local_int8; + svint8_t bad_init_int8 = for; // expected-error {{expected expression}} + +#if __cplusplus >= 201103L + int empty_brace_init_int = {}; + svint8_t empty_brace_init_int8 = {}; +#else + int empty_brace_init_int = {}; // expected-error {{scalar initializer cannot be empty}} + svint8_t empty_brace_init_int8 = {}; // expected-error {{scalar initializer cannot be empty}} +#endif + svint8_t brace_init_int8 = { local_int8 }; + svint8_t bad_brace_init_int8 = { local_int8, 0 }; // expected-error {{excess elements in scalar initializer}} + + const svint8_t const_int8 = local_int8; // expected-note {{declared const here}} + + const svint8_t uninit_const_int8; // expected-error {{default initialization}} + + volatile svint8_t volatile_int8; + + const volatile svint8_t const_volatile_int8; // expected-error {{default initialization}} expected-note {{declared const here}} + + _Atomic svint8_t atomic_int8; // expected-error {{_Atomic cannot be applied to incomplete type}} + __restrict svint8_t restrict_int8; // expected-error {{requires a pointer or reference}} + + svint8_t array_int8[1]; // expected-error {{array has sizeless element type}} + + bool test_int8 = init_int8; // expected-error {{cannot initialize}} + + int int_int8 = init_int8; // expected-error {{cannot initialize}} + + init_int8 = local_int8; + init_int8 = local_int16; // expected-error {{incompatible type}} + init_int8 = sel; // expected-error {{incompatible type}} + + sel = local_int8; // expected-error {{incompatible type}} + + local_int8 = (svint8_t)local_int8; + local_int8 = (const svint8_t)local_int8; + local_int8 = (svint8_t)local_int16; // expected-error {{not allowed}} + + init_int8 = local_int8; + init_int8 = const_int8; + init_int8 = volatile_int8; + init_int8 = const_volatile_int8; + + const_int8 = local_int8; // expected-error {{const-qualified type}} + + volatile_int8 = local_int8; + volatile_int8 = const_int8; + volatile_int8 = volatile_int8; + volatile_int8 = const_volatile_int8; + + const_volatile_int8 = local_int8; // expected-error {{const-qualified type}} + + init_int8 = sel ? init_int8 : local_int8; + init_int8 = sel ? init_int8 : const_int8; + init_int8 = sel ? volatile_int8 : const_int8; + init_int8 = sel ? volatile_int8 : const_volatile_int8; + + pass_int8(local_int8); + pass_int8(local_int16); // expected-error {{no matching function}} + + local_int8 = return_int8(); + local_int16 = return_int8(); // expected-error {{incompatible type}} + + dump(&local_int8); + dump(&const_int8); + dump(&volatile_int8); + dump(&const_volatile_int8); + + dump(&local_int8 + 1); // expected-error {{arithmetic on a pointer to an incomplete type}} + + *&local_int8 = local_int8; + *&const_int8 = local_int8; // expected-error {{read-only variable}} + *&volatile_int8 = local_int8; + *&const_volatile_int8 = local_int8; // expected-error {{read-only variable}} + + (&local_int8)[0] = local_int8; // expected-error {{subscript of pointer to incomplete type}} + + overf(local_int8); + overf(local_int16); + + overf8(local_int8); + overf8(local_int16); // expected-error {{no matching function}} + + overf16(local_int8); // expected-error {{no matching function}} + overf16(local_int16); + + varargs(1, local_int8, local_int16); + + +init_int8; // expected-error {{invalid argument type}} + ++init_int8; // expected-error {{cannot increment}} + -init_int8; // expected-error {{invalid argument type}} + --init_int8; // expected-error {{cannot decrement}} + ~init_int8; // expected-error {{invalid argument type}} + !init_int8; // expected-error {{invalid argument type}} + *init_int8; // expected-error {{requires pointer operand}} + __real init_int8; // expected-error {{invalid type}} + __imag init_int8; // expected-error {{invalid type}} + + local_int8 + init_int8; // expected-error {{invalid operands}} + local_int8 - init_int8; // expected-error {{invalid operands}} + local_int8 * init_int8; // expected-error {{invalid operands}} + local_int8 / init_int8; // expected-error {{invalid operands}} + local_int8 % init_int8; // expected-error {{invalid operands}} + local_int8 & init_int8; // expected-error {{invalid operands}} + local_int8 | init_int8; // expected-error {{invalid operands}} + local_int8 ^ init_int8; // expected-error {{invalid operands}} + local_int8 << init_int8; // expected-error {{invalid operands}} + local_int8 >> init_int8; // expected-error {{invalid operands}} + local_int8 < init_int8; // expected-error {{invalid operands}} + local_int8 <= init_int8; // expected-error {{invalid operands}} + local_int8 == init_int8; // expected-error {{invalid operands}} + local_int8 != init_int8; // expected-error {{invalid operands}} + local_int8 >= init_int8; // expected-error {{invalid operands}} + local_int8 > init_int8; // expected-error {{invalid operands}} + local_int8 && init_int8; // expected-error {{invalid operands}} expected-error {{not contextually convertible}} + local_int8 || init_int8; // expected-error {{invalid operands}} expected-error {{not contextually convertible}} + + local_int8 += init_int8; // expected-error {{invalid operands}} + local_int8 -= init_int8; // expected-error {{invalid operands}} + local_int8 *= init_int8; // expected-error {{invalid operands}} + local_int8 /= init_int8; // expected-error {{invalid operands}} + local_int8 %= init_int8; // expected-error {{invalid operands}} + local_int8 &= init_int8; // expected-error {{invalid operands}} + local_int8 |= init_int8; // expected-error {{invalid operands}} + local_int8 ^= init_int8; // expected-error {{invalid operands}} + local_int8 <<= init_int8; // expected-error {{invalid operands}} + local_int8 >>= init_int8; // expected-error {{invalid operands}} + + local_int8 + 0; // expected-error {{invalid operands}} + local_int8 - 0; // expected-error {{invalid operands}} + local_int8 * 0; // expected-error {{invalid operands}} + local_int8 / 0; // expected-error {{invalid operands}} + local_int8 % 0; // expected-error {{invalid operands}} + local_int8 & 0; // expected-error {{invalid operands}} + local_int8 | 0; // expected-error {{invalid operands}} + local_int8 ^ 0; // expected-error {{invalid operands}} + local_int8 << 0; // expected-error {{invalid operands}} + local_int8 >> 0; // expected-error {{invalid operands}} + local_int8 < 0; // expected-error {{invalid operands}} + local_int8 <= 0; // expected-error {{invalid operands}} + local_int8 == 0; // expected-error {{invalid operands}} + local_int8 != 0; // expected-error {{invalid operands}} + local_int8 >= 0; // expected-error {{invalid operands}} + local_int8 > 0; // expected-error {{invalid operands}} + local_int8 && 0; // expected-error {{invalid operands}} expected-error {{not contextually convertible}} + local_int8 || 0; // expected-error {{invalid operands}} expected-error {{not contextually convertible}} + + if (local_int8) {} // expected-error {{not contextually convertible}} + while (local_int8) {} // expected-error {{not contextually convertible}} + do {} while (local_int8); // expected-error {{not contextually convertible}} + switch (local_int8) { default:; } // expected-error {{integer type}} +} + +int vararg_receiver(int count, svint8_t first, ...) { + __builtin_va_list va; + + __builtin_va_start(va, first); + __builtin_va_arg(va, svint8_t); + __builtin_va_end(va); + return count; +} + +struct sized_struct { + int f1; + svint8_t f2; // expected-error {{field has incomplete type}} + svint8_t f3 : 2; // expected-error {{incomplete type}} +}; + +union sized_union { + int f1; + svint8_t f2; // expected-error {{field has incomplete type}} + svint8_t f3 : 2; // expected-error {{incomplete type}} +}; + +void pass_int8_ref(svint8_t &); // expected-note {{not viable}} + +svint8_t &return_int8_ref(); +#if __cplusplus >= 201103L +svint8_t &&return_int8_rvalue_ref(); +#endif + +template struct s_template { + T y; // expected-error {{incomplete type}} +}; + +template struct s_ptr_template { + s_ptr_template(); + s_ptr_template(T, svint8_t = svint8_t()); + s_ptr_template(const s_ptr_template &, svint8_t = svint8_t()); + T *y; +}; + +template struct s_array_template { + T y[1]; // expected-error {{array has sizeless element type}} +}; + +struct widget { + widget(s_ptr_template); + svint8_t operator[](int); +}; + +template +struct wrapper_iterator { + T operator++(); + T operator*() const; + bool operator!=(const wrapper_iterator &) const; +}; + +template +struct wrapper { + wrapper(); + operator T() const; + wrapper_iterator begin() const; + wrapper_iterator end() const; +}; + +#if __cplusplus >= 201103L +struct explicit_conv { + explicit operator svint8_t () const; +}; +#endif + +struct constructible_from_sizeless { + constructible_from_sizeless(svint8_t); +}; + +void with_default(svint8_t = svint8_t()); + +#if __cplusplus >= 201103L +constexpr int ce_taking_int8(svint8_t) { return 1; } // expected-error {{not a literal type}} +#endif + +#if __cplusplus < 201703L +void throwing_func() throw(svint8_t); // expected-error {{incomplete type}} +void throwing_pointer_func() throw(svint8_t *); // expected-error {{pointer to incomplete type}} +void throwing_pointer_func() throw(svint8_t &); // expected-error {{reference to incomplete type}} +#endif + +template void template_fn_direct(T) {} +template void template_fn_ref(T &) {} +template void template_fn_const_ref(const T &) {} +#if __cplusplus >= 201103L +template void template_fn_rvalue_ref(T &&) {} +#endif + +void cxx_only() { + svint8_t local_int8; + svint16_t local_int16; + + pass_int8_ref(local_int8); + pass_int8_ref(local_int16); // expected-error {{no matching function}} + + local_int8 = return_int8_ref(); + local_int16 = return_int8_ref(); // expected-error {{incompatible type}} + return_int8_ref() = local_int8; + return_int8_ref() = local_int16; // expected-error {{incompatible type}} + +#if __cplusplus >= 201103L + local_int8 = return_int8_rvalue_ref(); + local_int16 = return_int8_rvalue_ref(); // expected-error {{incompatible type}} + + return_int8_rvalue_ref() = local_int8; // expected-error {{not assignable}} + return_int8_rvalue_ref() = local_int16; // expected-error {{not assignable}} +#endif + + local_int8 = static_cast(local_int8); + local_int8 = static_cast(local_int16); // expected-error {{not allowed}} + local_int16 = static_cast(local_int8); // expected-error {{not allowed}} + + throw 1; + throw local_int8; // expected-error {{cannot throw object of incomplete type}} + + try {} catch (int) {} + try {} catch (svint8_t) {} // expected-error {{cannot catch incomplete type}} + try {} catch (svint8_t *) {} // expected-error {{cannot catch pointer to incomplete type}} + try {} catch (svint8_t &) {} // expected-error {{cannot catch reference to incomplete type}} + + new svint8_t; // expected-error {{allocation of incomplete type}} + new svint8_t(); // expected-error {{allocation of incomplete type}} + + new (global_int8_ptr) svint8_t; // expected-error {{allocation of incomplete type}} + new (global_int8_ptr) svint8_t(); // expected-error {{allocation of incomplete type}} + + local_int8.~__SVInt8_t(); // expected-error {{cannot be used in a pseudo-destructor}} + delete global_int8_ptr; // expected-error {{cannot delete expression of type}} + delete[] global_int8_ptr; // expected-error {{cannot delete expression of type}} + + (void)svint8_t(); + + local_int8 = svint8_t(); + local_int8 = svint16_t(); // expected-error {{incompatible type}} + + s_template st_int; + s_template st_svint8; // expected-note {{in instantiation}} + + s_ptr_template st_ptr_int; + s_ptr_template st_ptr_svint8; + + widget w(1); + local_int8 = w[1]; + + s_array_template st_array_int; + s_array_template st_array_svint8; // expected-note {{in instantiation}} + + local_int8 = static_cast(wrapper()); + local_int16 = static_cast(wrapper()); // expected-error {{incompatible type}} + + local_int8 = wrapper(); + local_int16 = wrapper(); // expected-error {{incompatible type}} + + svint8_t &ref_int8 = local_int8; + ref_int8 = ref_int8; // expected-warning {{to itself}} + ref_int8 = local_int8; + local_int8 = ref_int8; + +#if __cplusplus >= 201103L + svint8_t zero_init_int8 {}; + svint8_t init_int8 { local_int8 }; + svint8_t wrapper_init_int8 { wrapper() }; + svint8_t &ref_init_int8 { local_int8 }; + + template_fn_direct({ wrapper () }); +#endif + + template_fn_direct(local_int8); + template_fn_ref(local_int8); + template_fn_const_ref(local_int8); +#if __cplusplus >= 201103L + template_fn_rvalue_ref(local_int8); +#endif + +#if __cplusplus >= 201103L + constexpr svint8_t ce_int8_a = wrapper(); // expected-error {{constexpr variable cannot have non-literal type 'const svint8_t'}} +#endif + + (void) typeid (__SVInt8_t); + (void) typeid (__SVInt8_t *); + (void) typeid (local_int8); + (void) typeid (ref_int8); + (void) typeid (static_int8_ptr); + + _Static_assert(__is_trivially_destructible(svint8_t), ""); + _Static_assert(!__is_nothrow_assignable(svint8_t, svint8_t), ""); + _Static_assert(__is_nothrow_assignable(svint8_t &, svint8_t), ""); + _Static_assert(!__is_nothrow_assignable(svint8_t &, svint16_t), ""); + _Static_assert(__is_constructible(svint8_t), ""); + _Static_assert(__is_constructible(svint8_t, svint8_t), ""); + _Static_assert(!__is_constructible(svint8_t, svint8_t, svint8_t), ""); + _Static_assert(!__is_constructible(svint8_t, svint16_t), ""); + _Static_assert(__is_nothrow_constructible(svint8_t), ""); + _Static_assert(__is_nothrow_constructible(svint8_t, svint8_t), ""); + _Static_assert(!__is_nothrow_constructible(svint8_t, svint16_t), ""); + _Static_assert(!__is_assignable(svint8_t, svint8_t), ""); + _Static_assert(__is_assignable(svint8_t &, svint8_t), ""); + _Static_assert(!__is_assignable(svint8_t &, svint16_t), ""); + _Static_assert(__has_nothrow_assign(svint8_t), ""); + _Static_assert(__has_nothrow_move_assign(svint8_t), ""); + _Static_assert(__has_nothrow_copy(svint8_t), ""); + _Static_assert(__has_nothrow_constructor(svint8_t), ""); + _Static_assert(__has_trivial_assign(svint8_t), ""); + _Static_assert(__has_trivial_move_assign(svint8_t), ""); + _Static_assert(__has_trivial_copy(svint8_t), ""); + _Static_assert(__has_trivial_constructor(svint8_t), ""); + _Static_assert(__has_trivial_move_constructor(svint8_t), ""); + _Static_assert(__has_trivial_destructor(svint8_t), ""); + _Static_assert(!__has_virtual_destructor(svint8_t), ""); + _Static_assert(!__is_abstract(svint8_t), ""); + _Static_assert(!__is_aggregate(svint8_t), ""); + _Static_assert(!__is_base_of(svint8_t, svint8_t), ""); + _Static_assert(!__is_class(svint8_t), ""); + _Static_assert(__is_convertible_to(svint8_t, svint8_t), ""); + _Static_assert(!__is_convertible_to(svint8_t, svint16_t), ""); + _Static_assert(!__is_empty(svint8_t), ""); + _Static_assert(!__is_enum(svint8_t), ""); + _Static_assert(!__is_final(svint8_t), ""); + _Static_assert(!__is_literal(svint8_t), ""); + _Static_assert(__is_pod(svint8_t), ""); + _Static_assert(!__is_polymorphic(svint8_t), ""); + _Static_assert(__is_trivial(svint8_t), ""); + _Static_assert(__is_object(svint8_t), ""); + _Static_assert(__has_unique_object_representations(svint8_t), ""); + _Static_assert(!__is_arithmetic(svint8_t), ""); + _Static_assert(!__is_floating_point(svint8_t), ""); + _Static_assert(!__is_integral(svint8_t), ""); + _Static_assert(!__is_complete_type(svint8_t), ""); + _Static_assert(!__is_void(svint8_t), ""); + _Static_assert(!__is_array(svint8_t), ""); + _Static_assert(!__is_function(svint8_t), ""); + _Static_assert(!__is_reference(svint8_t), ""); + _Static_assert(__is_reference(svint8_t &), ""); + _Static_assert(__is_reference(const svint8_t &), ""); + _Static_assert(!__is_lvalue_reference(svint8_t), ""); + _Static_assert(__is_lvalue_reference(svint8_t &), ""); +#if __cplusplus >= 201103L + _Static_assert(!__is_lvalue_reference(svint8_t &&), ""); +#endif + _Static_assert(!__is_rvalue_reference(svint8_t), ""); + _Static_assert(!__is_rvalue_reference(svint8_t &), ""); +#if __cplusplus >= 201103L + _Static_assert(__is_rvalue_reference(svint8_t &&), ""); +#endif + _Static_assert(!__is_fundamental(svint8_t), ""); + _Static_assert(__is_object(svint8_t), ""); + _Static_assert(!__is_scalar(svint8_t), ""); + _Static_assert(!__is_compound(svint8_t), ""); + _Static_assert(!__is_pointer(svint8_t), ""); + _Static_assert(__is_pointer(svint8_t *), ""); + _Static_assert(!__is_member_object_pointer(svint8_t), ""); + _Static_assert(!__is_member_function_pointer(svint8_t), ""); + _Static_assert(!__is_member_pointer(svint8_t), ""); + _Static_assert(!__is_const(svint8_t), ""); + _Static_assert(__is_const(const svint8_t), ""); + _Static_assert(__is_const(const volatile svint8_t), ""); + _Static_assert(!__is_volatile(svint8_t), ""); + _Static_assert(__is_volatile(volatile svint8_t), ""); + _Static_assert(__is_volatile(const volatile svint8_t), ""); + _Static_assert(!__is_standard_layout(svint8_t), ""); + // At present these types are opaque and don't have the properties + // implied by their name. + _Static_assert(!__is_signed(svint8_t), ""); + _Static_assert(!__is_unsigned(svint8_t), ""); + +#if __cplusplus >= 201103L + auto auto_int8 = local_int8; + auto auto_int16 = local_int16; +#if __cplusplus >= 201703L + auto [auto_int8_a] = local_int8; // expected-error {{cannot decompose}} +#endif +#endif + + s_ptr_template y; + s_ptr_template &x = y; + + constructible_from_sizeless cfs1(local_int8); + constructible_from_sizeless cfs2 = local_int8; +#if __cplusplus >= 201103L + constructible_from_sizeless cfs3 { local_int8 }; +#endif + +#if __cplusplus >= 201103L + local_int8 = ([]() { return svint8_t(); })(); + local_int8 = ([]() -> svint8_t { return svint8_t(); })(); + auto fn1 = [&local_int8](svint8_t x) { local_int8 = x; }; + auto fn2 = [&local_int8](svint8_t *ptr) { *ptr = local_int8; }; + auto fn3 = [local_int8](svint8_t *ptr) { *ptr = local_int8; }; // expected-error {{by-copy capture}} + + for (auto x : local_int8) {} // expected-error {{incomplete type}} + for (auto x : wrapper()) { (void)x; } + for (const svint8_t &x : wrapper()) { (void)x; } // expected-warning {{always a copy}} expected-note {{use non-reference type}} + for (const svint8_t x : wrapper()) { (void)x; } +#endif +} + +#if __cplusplus >= 201103L +svint8_t ret_bad_conv() { return explicit_conv(); } // expected-error {{no viable conversion from returned value of type 'explicit_conv' to function return type 'svint8_t'}} +#endif