Basic Concepts[Basic]

6 Basic Concepts[Basic]

NOTE

HLSL inherits a significant portion of its language semantics from C and C++. Some of this is a result of intentional adoption of syntax early in the development of the language and some a side-effect of the Clang-based implementation of DXC.

For clarity, this specification includes all necessary definitions even if they are not different from the isoC or isoCPP standard.

6.1 Preamble[Basic.Preamble]

An entity is a value, object, function, enumerator, type, class member, bit-field, template, template specialization, namespace, or pack.

A name is a use of an identifier (8.2.4), operator-function-id ([Overload.Operator]), conversion-function-id ([Classes.Conversions]), or template-id ([Template]) that denotes any entity or label ([Stmt.Label]).

Every name that denotes an entity is introduced by a declaration. Every name that denotes a label is introduced by a labeled statement ([Stmt.Label]).

A variable is introduced by the declaration of a reference other than a non-static data member of an object. The variable’s name denotes the reference or object.

Name lookup is the process of determining if a name refers to a type or template, and if so, which type or template it refers to. Name lookup occurs any time a name is encountered during parsing of a translation unit.

Two names are the same name if:

  • they are identifiers composed of the same character sequence, or

  • they are operator-function-ids formed with the same operator, or

  • they are conversion-function-ids formed with the same type, or

  • they are template-ids that refer to the same class or function.

6.2 Declarations and definitions[Basic.Decl]

A declaration (9) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. If a declaration introduces names, it specifies the interpretation and attributes of these names. A declaration may also have effects such as:

  • verifying a static assertion (9),

  • use of attributes (9), and

  • controlling template instantiation ([Template.Inst]).

A declaration is a definition unless:

  • it declares a function without specifying the function’s body ([Decl.Function]),

  • it is a parameter declaration in a function declaration that does not specify the function’s body ([Decl.Function]),

  • it is a global or namespace member declaration without the static specifier,

  • it declares a static data member in a class definition,

  • it is a class name declaration,

  • it is a template parameter,

  • it is a typedef declaration (9),

  • it is an alias-declaration (9),

  • it is a using-declaration (9),

  • it is a static_assert-declaration (9),

  • it is an empty-declaration (9),

  • or a using-directive (9).

NOTE Global variable declarations are implicitly constant and external in HLSL, unless they are declared with the static storage specifier.

The two examples below are adapted from isoCPP [basic.def]. All but one of the following are definitions:

int f(int x) return x+1; // defines f and x struct S int a;int b;; // defines S, S::a, and S::b struct X // defines X int x; // defines non-static member x static int y; // declares static data member y ; int X::y = 1; // defines X::y enum up, down ; // defines up and down namespace N // defines N int d; // declares N::d static int i; // defines N::i

All of the following are declarations:

int a; // declares a const int c; // declares c X anX; // declares anX int f(int); // declares f struct S; // declares S typedef int Int; // declares Int using N::d; // declares d using Float = float; // declares Float cbuffer CB // does not declare CB int z; // declares z tbuffer TB // does not declare TB int w; // declares w

6.3 One-Definition Rule[Basic.ODR]

The isoCPP One-definition rule is adopted as defined in isoCPP [basic.def.odr].

6.4 Scope[Basic.Scope]

A valid name is a name that may be used unqualified to refer to a specific entity.

The declarative region of a declaration is the largest part of a program in which the name is valid.

The scope of a name is the possibly discontiguous region of a program in which the name may be used as an unqualified name to refer to the same entity. The scope of a declaration is the same as its declarative region unless another declaration of the same name exists within that region, in which case the declarative region of the inner declaration is excluded from the outer declaration’s scope.

Names declared by a declaration are introduced into the scope in which the declaration occurs except for members of cbuffer-declarations ([Decl.cbuffer]), and using-declarations ([Decl.Using]) which do not follow this general behavior.

Within a declarative region, if multiple declarations introduce the same name either;

  • all the declarations shall refer to the same entity; or

  • all refer to functions or function templates; or

  • one declaration shall declare a class or enumeration name; and

    • the other declarations shall refer to the same entity; or

    • all refer to functions or function templates, hiding the class or enumeration name.

Scopes nest within each other. A containing scope, is a scope that fully contains another scope which is said to be contained. The smallest containing scope of S is the parent scope of S, except for template parameter scopes, where the parent scope will be the smallest containing scope that is not a template parameter scope.

A declaration’s target scope is the scope its point of declaration is contained in. Any names introduced by a declaration are bound to its target scope.

6.4.1 Point of Declaration[Basic.Scope.Point]

The point of declaration for a name marks the location in a program where the name is introduced. Any use of the name after the point of declaration before the end of the scope the declaration is introduced in may refer to the declared entity.

Except for the cases identified in the remainder of this subclause, the point of a declaration for a name is immediately after its declarator, and before its initializer.

The point of declaration for a class name or class template name introduced with a class-specifier is immediately after the identifier or simple-template-id.

The point of declaration for an enumeration name is immediately after the identifier in the enumeration declaration. The point of declaration for an enumerator is immediately after its definition.

The name of a class member may be looked up in a class immediately after its point of declaration, even if the containing class is incomplete.

The point of declaration for a name introduced in an elaborated-type-specifier that is a forward declaration is immediately after the identifier, and the name is declared to be a class-name in the scope containing the forward declaration. Otherwise, if the elaborated-type-specifier is used in a decl-specifier-seq or parameter-declaration-clause during a function declaration, then the name is declared to be a class-name in the namespace that contains the function declaration.

The point of declaration for an injected-class-name is immediately following the opening brace of the definition of the class definition.

The point of declaration of a template parameter is immediately after its template-parameter.

6.4.2 Block Scope[Basic.Scope.Block]

A block scope is introduced by either a compound-statement, or a selection or iteration statement ([Stmt.If], [Stmt.Switch], [Stmt.For], [Stmt.While]), or a substatement of such a statement.

A name declared inside a block is said to have block-scope and is local to that block. Its potential scope begins at its point of definition and ends at the end of the block. A variable declared within a block is called a local variable.

The potential scope of a function parameter name begins at its point of declaration. A parameter name shall not be redeclared in the outermost block of the function definition.

Names declared in selection and iteration statements are local to the statement they are declared in, and shall not be redeclared in any subsequent conditions of the statement or the outermost block of the statement.

6.4.3 Function Prototype Scope[Basic.Scope.Prototype]

In a function declarator that has no function definition, the names of parameters are optional and have function prototype scope, which ends at the end of the declarator.

6.4.4 Namespace Scope[Basic.Scope.Namespace]

The declarative region of a namespace defined by a namespace-definition is its namespace-body. The potential scope referenced by a namespace-name is the concatenation of all declarative regions for each namespace-definition with that name.

Declarations within a namespace-body declare entities that are said to be members of the namespace. Any names introduced by these declarations are said to be member names of the namespace, and they have namespace scope.

The potential scope of a namespace member name begins at its point of declaration and contains the remainder of the namespace scope through the end of the translation unit. Each using-directive nominating a namespace extends the potential scope of namespace members in the nominated namespace to include the portion of the potential scope containing the using-directive following the member’s point of declaration.

A member of a namespace may also be referred to as a qualified name any point after its point of declaration via the rules defined in 6.5.3.2.

The outermost declarative region of a translation unit is the implicit global namespace. All entities declared in the global namespace are said to be global declarations. The names of global declarations are said to have global namespace scope, and are said to be global names.

The potential scope of a name with global namespace scope begins at the name’s point of declaration and extends to the end of the translation unit.

6.4.5 Class Scope[Basic.Scope.Class]

The declarative region of a class is defined by the curly braces ({ }) enclosing its optional member-specialization. Declarations within a class’s member-specialization are said to be members of that class. Any names introduced by these declarations are said to be member names of the class, and they have class scope.

The potential scope of a member name of a class includes the declarative region of the class after the name’s point of declaration, and the function bodies, default arguments, and initializers of non-static data members in the class.

A name used in a class shall refer to the same declaration at the point of its use as when re-evaluated with the completed class.

6.4.6 Enumeration Scope[Basic.Scope.Enumeration]

The name of a scoped enumerator ([Decl.Enum]), has enumeration scope. Its potential scope begins at its point of declaration and terminates at the end of the enumerator-list in the enumeration’s declaration.

6.4.7 Template Parameter Scope[Basic.Scope.Template]

Each template-parameter and template-declaration introduces a template parameter scope. A template-parameter’s introduced scope contains the smallest template-parameter-list containing the template-parameter, and extends to include the template-declaration’s scope. A template-declaration’s introduced scope begins at its template-parameter-list and extends to the end of the template-declaration.

Any declaration outside the template-parameter-list of a template-declaration targets the parent scope of the template-declaration.

The outermost scope of all declarations is the global scope, while the innermost scope of a declaration is the scope the declaration appears within.

6.5 Name Lookup[Basic.Lookup]

Name lookup unambiguously identifies one or more declarations associated with a name, the name may only refer to more than one declaration if the name is a function name in which case the set of declarations form an overload set ([Overload.Decl]). When applicable, overload resolution takes place after name lookup.

Name lookup in the context of an expression is unqualified name lookup in the enclosing scope of the expression.

6.5.1 Unqualified Name Lookup[Basic.Lookup.Unqualified]

For unqualified name lookup, scopes are searched in a specified order from the innermost scope to the outermost scope, and lookup ends as soon as a matching declaration is found. If no declaration is found, the program is ill-formed.

Declarations of a namespace named in a using-directive are visible in the enclosing namespace, and for the purposes of name lookup they are considered to be members of that enclosing namespace.

An unqualified name used as a function name in a function call expression is resolved using argument dependent name lookup (6.5.2).

A name used in global scope or namespace scope outside the body of any function or class shall be declared before its use in the scope it is declared in or any enclosing scope.

A name used in the definition of a non-member function shall be declared before its use, and the declaration must be in the block in which the name is used, an enclosing block, or an enclosing scope of the function declaration (enclosing namespace or global scope).

A name used in the definition of a class shall be declared either before its use in the class or as a member of a base class or before the definition of the class in an enclosing scope or enclosing class declaration.

A name used in the definition of a member function of a class shall be declared in one of the following ways:

  • before its use in the block in which it is used or an enclosing block, or

  • shall be a member of the class being declared or a member of a base class of that class, or

  • if the class being declared is a nested class of another class, the name shall be a member of the enclosing class or a base class of the enclosing class, or

  • if the class being declared is a local class or is declared within the scope of an enclosing local class, the name shall be declared before the definition of the class in an enclosing block, or

  • the name shall be declared before the use of the name in an enclosing namespace or at global scope.

During name lookup for a name used as a default argument for a function parameter declaration, the names of previously declared function parameters are visible.

During name lookup of a name used as the initializer of an enumerator, the names of previously declared enumerators are visible.

During name lookup of a name used as the initializer for a variable member of a namespace that is defined outside the namespace, name lookup behaves as if the definition occurs inside the namespace.

6.5.2 Argument Dependent Name Lookup[Basic.Lookup.Argument]

When a function is named as an unqualified name, a set of associated classes and namespaces for each function argument will be searched. The set of associated classes and namespaces for a given argument is constructed by the following:

  • If the type of the argument is a fundamental type the set of classes and the set of namespaces are empty.

  • If the argument is of class type, the set of associated classes will include the class of the argument, it’s direct and indirect base classes, and any classes it is a member of. The set of associated namespaces will include the enclosing namespaces of all the associated classes. If the argument’s type is a template specialization it will also include the associated classes and namespaces of template type parameters, but will not recurse into further template specializations.

  • If the argument is of enumeration type, the associated classes will be any enclosing classes of the enumeration declaration, and the namespaces will be any enclosing namespaces of the enumeration declaration.

If unqualified lookup of a function name returns a declaration of a class member, a block-scope function declaration that is not a using declaration, or a declaration that is neither a function nor a function template, argument dependent name lookup is skipped. Otherwise, the union of the declarations found with unqualified name lookup and the declarations found with argument dependent lookup are the full set of declarations with that name.

When looking up names in associated namespaces, using-directives are ignored, and all names except functions and function templates are ignored.

6.5.3 Qualified Name Lookup[Basic.Lookup.Qualified]

A member of a class, a member of a namespace, or an enumerator may be specified by name after a nested-name-specifier followed by the scope resolution operator (::).

If the declarator-id of a declaration is a qualified name, names looked up before the qualified name are looked up in the defining scope; names looked up after the qualified name are looked up in the scope of the qualified name’s class or namespace.

The unary scope operator (::) denotes that a name should be looked up in the global scope of the translation unit where it is used. Only names declared in the global namespace or visible to the global namespace will be resolved.

If the nested-name-specifier of a scope resolution operator names an enumeration, the name shall name an enumerator of that enumeration.

6.5.3.1 Class Members[Basic.Lookup.Qualified.Class]

In a qualified-id when the nested-name-specifier names a class the unqualified-id, the name is looked up in the scope of the class except that:

  • the names in any template arguments are looked up in the context of the containing expression.

  • if the name is specified in a using-declaration, class and enumeration names hidden within the same scope are found.

Class members hidden by names of derived classes or names declared in nested scopes can still be found if fully qualified with the scope resolution operator.

6.5.3.2 Namespace Members[Basic.Lookup.Qualified.Namespace]

In a qualified-id when the nested-name-specifier names a namespace as the unqualified-id|, the name is looked up in the scope of the namespace. If the qualified-id starts with ::, the name following the unary scope resolution operator is looked up in the global namespace. The names in any template arguments are looked up in the context of the containing expression.

Namespace-qualified lookup for a name will first identify all names directly declared in the specified namespace ignoring namespaces specified by a using-directive. If a non-zero number of names are found that is the result of name lookup, otherwise any namespaces nominated by a using-directive are searched.

namespace Bird void Quack() void Chirp()

namespace Duck void Quack() using namespace Bird;

export void f() Duck::Quack(); // resolves Duck::Quack Duck::Chirp(); // resolves Bird::Chirp

NOTE In the example above, both qualified-id expressions in the function f denote the namespace Duck, the Quack call resolves in the Duck namespace because it is declared directly there, and the function in the Bird namespace is not considered or added to any overload candidate set. Meanwhile, the Chirp call resolves against the Bird namespace because the namespace is exposed via a using-directive but the name is not declared in the Duck namespace directly.

Referenced namespaces are only searched once allowing programs that have namespaces cyclicly referencing each other to be well-formed.

During qualified lookup of namespace member names, if lookup finds multiple declarations of a name all declared in the same namespace, and one of the declarations introduces a class or enumeration name, while the other declarations refer to the same variable; the non-type name hides the type name. If lookup identifies multiple different type names (from different namespaces), or names that name different variables, the program is ill-formed.

In a declaration for a namespace member, the declarator-id must directly name a member of the namespace specified by the nested-name-specifier, no names exposed in the namespace through a using-directive are considered. The name lookup may rely on a using-directive to resolve the declarator-id.

namespace Fluffy void fluff();

namespace Cats using namespace Fluffy; namespace Snowball void pet();

void Cats::fluff() // ill-formed, fluff is not a member of Cats.

using namespace Cats; void Snowball::pet() // well-formed, Snowball is looked up in Cats.

6.5.4 Elaborated Type Specifiers[Basic.Lookup.Elaborated]

An elaborated-type-specifier may refer to a class-name or enum-name that is hidden by a non-type declaration.

An elaborated-type-specifier of the form:

class-key attribute-specifier-seqopt identifier ;

is called a class forward declaration. A class forward declaration may appear before or after the named class is defined, and may appear multiple times referring to the same class.

If an elaborated-type-specifier has no nested-name-specifier and is not a class forward declaration, the name is looked up as an unqualified name (6.5.1) except that any non-type names are ignored.

If an elaborated-type-specifier is specified with the enum keyword and the lookup does not find a previously declared type name, the program is ill-formed.

If an elaborated-type-specifier is specified with a class-key and unqualified lookup does not find a previously declared name, or if the specifier is a forward class declaration, the elaborated-type-specifier introduces the class-name.

If an elaborated-type-specifier has a nested-name-specifier, the name is looked up as a qualified name (6.5.3) except that any non-type names are ignored. In this form, if the name lookup does not find a previously declared name, the elaborated-type-specifier is ill-formed.

6.5.5 Class Member Access[Basic.Lookup.ClassMember]

In a class member access expression ([Expr.Post.Member]), if the expression matches the form:

postfix-expression . identifier

and the next token is a <, the identifier must be looked up to determine if the < token is the start of a template argument list or the binary less-than operator. The lookup is performed first in the context of the class type of the postfix-expression to the left of the . operator, then in the context of the complete postfix-expression.

If the id-expression of a class member access expression is an unqualified-id, the name is looked up in the scope of the class type of the expression on the left of the . operator.

If the id-expression of a class member access expression is a qualified-id, the qualified name is first looked up in the scope of the class type of the expression on the left of the . operator, and if not found it is looked up in the context of the complete postfix-expression.

namespace Animal struct Dog int Fur; ;

namespace Pet struct Dog : Animal::Dog int Fur; ;

export void fn() Pet::Dog s; s.Fur = 2; // Refers to Pet::Dog::Fur s.Dog::Fur = 3; // Refers to Pet::Dog::Fur s.Animal::Dog::Fur = 5; // Refers to Animal::Dog::Fur

NOTE The example above demonstrates qualified and unqualified name lookup for class members. The first and second references to Fur refer to the Pet namespace. The first through unqualified lookup, the second through qualified lookup in the context of s. The third demonstrates qualified lookup of a fully-qualified name.

If the nested-name-specifier contains a simple-template-id, the names of any template-arguments are looked up in the context of the complete postfix-expression.

6.5.6 Using-directives[Basic.Lookup.Using]

In a using-directive when looking up a namespace-name or the name in a nested-name-specifier, only namespace names are considered.

6.6 Storage Duration[Basic.Storage]

The storage duration of an object is the portion of the program’s execution time during which the object exists in memory. An object may have one of the following storage durations:

  • static storage duration

  • automatic storage duration

  • program storage duration

  • groupshared storage duration

6.6.1 Static Storage Duration[Basic.Storage.Static]

An object whose name is declared with the static storage specifier has static storage duration. Such an object is created when the thread begins execution and destroyed when the thread ends execution.

6.6.2 Automatic Storage Duration[Basic.Storage.Auto]

An object whose name is declared in a block (including function parameters) without the static storage specifier has automatic storage duration. Such an object is created when the block in which it is declared is entered and destroyed when the block is exited.

6.6.3 Program Storage Duration[Basic.Storage.Program]

An object whose name is declared in a global, namespace, or cbuffer scope without the static storage specifier has program storage duration. Such an object is created when the program begins execution and destroyed when the program ends execution.

6.6.4 Groupshared Storage Duration[Basic.Storage.Groupshared]

An object whose name is declared with the groupshared storage specifier has groupshared storage duration. Such an object is created when the thread group begins execution and destroyed when the thread group ends.

6.7 Program and linkage[Basic.Linkage]

A translation unit (5.1) is comprised of a sequence of declarations:

translation-unit:
declaration-sequenceopt

A program is one or more translation units linked together. A program built from a single translation unit, bypassing a linking step is called freestanding.

A program is said to be fully linked, when it contains no unresolved external declarations, and all exported declarations are entry point declarations (6.8). A program is said to be partially linked, when it contains at least one unresolved external declaration or at least one exported declaration that is not an entry point.

An implementation may generate programs as fully linked or partially linked as requested by the user, and a runtime may allow fully linked or partially linked programs as the implementation allows.

A name has linkage if it can refer to the same entity as a name introduced by a declaration in another scope. If a variable, function, or another entity with the same name is declared in several scopes, but does not have sufficient linkage, then several instances of the entity are generated.

  • A name with no linkage may not be referred to by names from any other scope.

  • A name with internal linkage may be referred to by names from other scopes within the same translation unit.

  • A name with external linkage may be referred to by names from other scopes within the same translation unit, and by names from scopes of other translation units.

  • A name with program linkage may be referred to by names from other scopes within the same translation unit, by names from scopes of other translation units, by names from scopes of other programs, and by a runtime implementation.

When merging translation units through linking or generating a freestanding program only names with program linkage must be retained in the final program.

6.7.1 Program Linkage[Basic.Linkage.Program]

Entities with program linkage can be referred to from other partially linked programs or a runtime implementation.

The following entities have program linkage:

  • entry point functions (6.8)

  • functions marked with export keyword ([Decl.Export])

  • declarations contained within an export-declaration-group ([Decl.Export])

6.7.2 External Linkage[Basic.Linkage.External]

Entities with external linkage can be referred to from the scopes in the other translation units and enable linking between them.

The following entities in HLSL have external linkage:

  • global variables that are not marked static or groupshared

  • static data members of classes or template classes

Linkage of functions (including template functions) that are not entry points or marked with export keyword is implementation dependent.

6.7.3 Internal Linkage[Basic.Linkage.Internal]

Entities with internal linkage can be referred to from all scopes in the current translation unit.

The following entities in HLSL have internal linkage:

  • global variables marked as static or groupshared

  • all entities declared in an unnamed namespace or a namespace within an unnamed namespace

  • enumerations

  • classes or template classes, their member functions, and nested classes and enumerations

6.7.4 No Linkage[Basic.Linkage.NoLinkage]

An entity with no linkage can be referred to only from the scope it is in.

Any of the following entites declared at function scope or block scopes derived from function scope have no linkage:

  • local variables

  • local classes and their member functions

  • other entities declared at function scope or block scopes derived from function scope that such as typedefs, enumerations, and enumerators

6.8 Start[Basic.Start]

A fully linked program shall contain one or more global functions, which are the designated starting points for the program. These global functions are called entry points, because they denote the location where execution inside the program begins.

Entry point functions have different requirements based on the target runtime and execution mode (6.8.1).

Parameters to entry functions and entry function return types must be of scalar, vector, matrix, non-intangible class type (6.9), or array of such types. Scalar and vector parameters and return types must be annotated with semantic annotations ([Decl.Attr.Semantic]). Class type input and output parameters must have all fields annotated with semantic annotations.

6.8.1 Execution Mode[Basic.Start.Mode]

A runtime may define a set of execution modes in an implementation-defined way. Each execution mode will have a set of implementation-defined rules which restrict available language functionality as appropriate for the execution mode.

6.9 Types[Basic.Types]

The object representation of an object of type T is the sequence of N bytes taken up by the object of type T, where N equals sizeof(T). The object representation of an object may be different based on the memory space it is stored in ([Intro.Memory.Spaces]).

NOTE sizeof(T) returns the size of the object as if it is stored in device memory, and determining the size of an object stored in another memory space is not currently possible.

The value representation of an object is the set of bits that hold the value of type T. Bits in the object representation that are not part of the value representation are padding bits.

An object type is a type that is not a function type, not a reference type, and not a void type.

A class type is a data type declared with either the class or struct keywords ([Classes]). A class type T may be declared as incomplete at one point in a translation unit via a forward declaration, and complete later with a full definition. The type T is the same type throughout the translation unit.

There are special implementation-defined types such as handle types, which fall into a category of standard intangible types. Intangible types are types that have no defined object representation or value representation, as such the size is unknown at compile time. Usage restrictions for objects of intangible type are documented in 6.9.3.

A class type T is an intangible class type if it contains a base class or members of intangible class type, standard intangible type, or arrays of such types. Standard intangible types and intangible class types are collectively called intangible types([Intangible]).

An object type is an incomplete type if the compiler lacks sufficient information to determine the size of an object of type T, and it is not an intangible type. It is a complete type if the compiler has sufficient information to determine the size of an object of type T, or if the type is known to be an intangible type. An object may not be defined to have an incomplete type.

Arithmetic types (6.9.1), enumeration types, and cv-qualified versions of these types are collectively called scalar types.

Vectors of scalar types declared with the built-in vector<T,N> template are vector types. An implementation must support vector lengths between 1 and 4 (i.e. 1 ≤ N ≤ 4 ).

NOTE An implementaiton may support vector lengths greater than 4.

Matrices of scalar types declared with the built-in matrix<T,N,M> template are matrix types. Matrix dimensions, N and M, must be between 1 and 4 (i.e. 1 ≤ N ≤ 4 ).

6.9.1 Arithmetic Types[Basic.Types.Arithmetic]

There are three standard signed integer types: int16_t, int32_t, and int64_t. Each of the signed integer types is explicitly named for the size in bits of the type’s object representation. There is also the type alias int which is an alias of int32_t. There is one minimum precision signed integer type: min16int. The minimum precision signed integer type is named for the required minimum value representation size in bits. The object representation of min16int is int. The standard signed integer types and minimum precision signed integer type are collectively called signed integer types.

There are three standard unsigned integer types: uint16_t, uint32_t, and uint64_t. Each of the unsigned integer types is explicitly named for the size in bits of the type’s object representation. There is also the type alias uint which is an alias of uint32_t. There is one minimum precision unsigned integer type: min16uint. The minimum precision unsigned integer type is named for the required minimum value representation size in bits. The object representation of min16uint is uint. The standard unsigned integer types and minimum precision unsigned integer type are collectively called unsigned integer types.

The minimum precision signed integer types and minimum precision unsigned integer types are collectively called minimum precision integer types. The standard signed integer types and standard unsigned integer types are collectively called standard integer types. The signed integer types and unsigned integer types are collectively called integer types. Integer types inherit the object representation of integers defined in isoC23, which requires twos’ complement representation. Integer types shall satisfy the constraints defined in isoCPP, section basic.fundamental.

There are three standard floating point types: half, float, and double. The float type is a 32-bit floating point type, and has a type alias float32_t. The double type is a 64-bit floating point type, and has a type alias float64_t. Both the float and double types have object representations as defined in IEEE754. The half type may be either 16-bit or 32-bit as controlled by implementation-defined compiler settings. If half is 32-bit it will have an object representation as defined in IEEE754, otherwise it will have an object representation matching the binary16 format defined in IEEE754. When half is 16-bit, it will have a type alias float16_t. There is one minimum precision floating point type: min16float. The minimum precision floating point type is named for the required minimum value representation size in bits. The object representation of min16float is float. The standard floating point types and minimum precision floating point type are collectively called floating point types.

Integer and floating point types are collectively called arithmetic types.

The void type is inherited from isoCPP, which defines it as having an empty set of values and being an incomplete type that can never be completed. The void type is used to signify the return type of a function that returns no value. Any expression can be explicitly converted to void.

6.9.2 Scalarized Type Compatability[Basic.Types.Scalarized]

All types T have a scalarized representation, SR(T), which is a list of one or more types representing each scalar element of T.

Scalarized representations are determined as follows:

  • The scalarized representation of an array T[n] is SR(T0), ..SR(Tn − 1).

  • The scalarized representation of a vector vector<T,n> is T0, ..Tn − 1.

  • The scalarized representation of a matrix matrix<T,n, m> is T0, ..T(n×m) − 1.

  • The scalarized representation of a class type T, SR(T) is computed recursively as SR(T::base), SR(T::0), ..SR(T::n) where (T::base) is T’s base class if it has one, and T : :n represents the n non-static members of T.

  • The scalarized representation for an enumeration type is the underlying arithmetic type.

  • The scalarized representation for arithmetic, intangible types, and any other type T is T.

Two types cv1 T1 and cv2 T2 are scalar-layout-compatible types if T1 and T2 are the same type or if the sequence of types defined by the scalar representation SR(T1) and scalar representation SR(T2) are identical.

6.9.3 Usage of Intangible Types[Basic.Types.Intangible]

The following usage restrictions apply to intangible types:

  • Instances of objects of intangible type may only be declared in the Thread address space ([Intro.Memory.Spaces]).

  • An object of intangible type may not be loaded or stored to any address space other than the Thread address space ([Intro.Memory.Spaces]).

  • An object of intangible type may not be a parameter or return type of a function with program linkage or external linkage (6.7.1 and 6.7.2).

6.10 Lvalues and rvalues[Basic.lval]

Expressions are classified by the type(s) of values they produce. The valid types of values produced by expressions are:

  1. An lvalue represents a function or object.

  2. An rvalue represents a temporary object.

  3. An xvalue (expiring value) represents an object near the end of its lifetime.

  4. A cxvalue (casted expiring value) is an xvalue which, on expiration, assigns its value to a bound lvalue.

  5. A glvalue is an lvalue, xvalue, or cxvalue.

  6. A prvalue is an rvalue that is not an xvalue.