public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [00/10][RFC] Splitting the C and C++ concept of "complete type"
@ 2018-10-15 14:32 Richard Sandiford
  2018-10-15 14:33 ` [01/10] Expand COMPLETE_TYPE_P in obvious checks for null Richard Sandiford
                   ` (11 more replies)
  0 siblings, 12 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

The C standard says:

    At various points within a translation unit an object type may be
    "incomplete" (lacking sufficient information to determine the size of
    objects of that type) or "complete" (having sufficient information).

For AArch64 SVE, we'd like to split this into two concepts:

  * has the type been fully defined?
  * would fully-defining the type determine its size?

This is because we'd like to be able to represent SVE vectors as C and C++
types.  Since SVE is a "vector-length agnostic" architecture, the size
of the vectors is determined by the runtime environment rather than the
programmer or compiler.  In that sense, defining an SVE vector type does
not determine its size.  It's nevertheless possible to use SVE vector types
in meaningful ways, such as having automatic vector variables and passing
vectors between functions.

The main questions in the RFC are:

  1) is splitting the definition like this OK in principle?
  2) are the specific rules described below OK?
  3) coding-wise, how should the split be represented in GCC?

Terminology
-----------

Going back to the second bullet above:

  * would fully-defining the type determine its size?

the rest of the RFC calls a type "sized" if fully defining it would
determine its size.  The type is "sizeless" otherwise.

Contents
--------

The RFC is organised as follows.  I've erred on the side of including
detail rather than leaving it out, but each section is meant to be
self-contained and skippable:

  - An earlier RFC
  - Quick overview of SVE
  - Why we need SVE types in C and C++
  - How we ended up with this definition
  - The SVE types in more detail
  - Outline of the type system changes
  - Sizeless structures (and testing on non-SVE targets)
  - Other variable-length vector architectures
  - Edits to the C standard
    - Base changes
    - Updates for consistency
    - Sizeless structures
  - Edits to the C++ standard
  - GCC implementation questions

I'll follow up with patches that implement the split.



An earlier RFC
==============

For the record (in case this sounds familiar) I sent an RFC about the
sizeless type extension a while ago:

    https://gcc.gnu.org/ml/gcc/2017-08/msg00012.html

The rules haven't changed since then, but this version includes more
information and includes support for sizeless structures.


Quick overview of SVE
=====================

SVE is a vector extension to AArch64.  A detailed description is
available here:

    https://static.docs.arm.com/ddi0584/a/DDI0584A_a_SVE_supp_armv8A.pdf

but the only feature that really matters for this RFC is that SVE has no
fixed or preferred vector length.  Implementations can instead choose
from a range of possible vector lengths, with 128 bits being the minimum
and 2048 bits being the maximum.  Priveleged software can further
constrain the vector length within the range offered by the implementation;
e.g. linux currently provides per-thread control of the vector length.


Why we need SVE types in C and C++
==================================

SVE was designed to be an easy target for autovectorising normal scalar
code.  There are also various language extensions that support explicit
data parallelism or that make explicit vector chunking easier to do in
an architecture-neutral way (e.g. C++ P0214).  This means that many users
won't need to do anything SVE-specific.

Even so, there's always going to be a place for writing SVE-specific
optimisations, with full access to the underlying ISA.  As for other
vector architectures, we'd like users to be able to write such routines
in C and C++ rather than force them to go all the way to assembly.

We'd also like C and C++ functions to be able to take SVE vector
parameters and return SVE vector results, which is particularly useful
when implementing things like vector math routines.  In this case in
particular, the types need to map directly to something that fits in
an SVE register, so that passing and returning vectors has minimal
overhead.


How we ended up with this definition
====================================

Requirements
------------

We need the SVE vector types to define and use SVE intrinsic functions
and to write SVE vector library routines.  The key requirements when
defining the types were:

  * They must be available in both C and C++ (because we want to be able
    add SVE optimisations to C-only codebases).

  * They must fit in an SVE vector register (so there can be no on-the-side
    information).

  * 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's what intrinsics and vector library routines need to do).

  * It must be possible to use the types in _Generic associations
    (so that _Generic can be used to provide tgmath.h-style overloads).

  * It must be possible to use 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++).

Ideally, there'd also be a way of grouping SVE vectors together into tuples,
since the ISA has instructions like LD2 that return multiple vectors.
It would be good if users could also define their own tuple types, on top
of the ones needed by the intrinsics, although that's more "nice to have".

Possible approaches
-------------------

The main complication is that the size of an SVE vector is not a
compile-time constant.  It seems that any approach to handling this
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).

Why (2) seemed like a bad idea
------------------------------

(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 declarations of functions that
pass or return variable-length types.

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 (unseen) extensions, even those that are aware of
variable-length arrays.  Also, AIUI, variable-length arrays were added
to an early draft of C++14, but were later removed as too controversial
and didn't 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.

Why (3) seemed like a bad idea
------------------------------

(3) can be divided into two:

(3a) The vector types have a constant size and are large enough for all
     possible vector lengths.

    The main problem with this is that the maximum size of an SVE
    vector (2048 bits) is much larger than the minimum size (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:

	struct s { _Alignas(16) int i; };

    except that the amount of padding would only be known at runtime.

    There's also a significant conceptual problem: encoding a fixed size
    goes against a guiding principle of SVE, in which there is no preferred
    vector length.  There's 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.

(3b) The vector types have a constant size and refer to separate storage
     (as for std::string etc.)

    This would be difficult to do without C++-style constructor, destructor,
    copy and move semantics, so wouldn't work well in C.  And in C++ it would
    be less efficient than the proposed approach, since presumably an Allocator
    would be needed to allocate the separate storage.  It would also require
    a complicated ABI mapping to ensure that the vectors can still be passed
    and returned in registers.

Chosen approach
---------------

We therefore took approach (1) and classified C and C++ types as "sized"
(having a measurable size when fully-defined) and "sizeless" (never
having a measurable size).  Sizeless types have no defined size,
alignment or layout at the language level, with those things becoming
purely an ABI-level detail.

We then treated all sizeless types as permanently incomplete.
On its own, this would put them in a similar situation to "void"
(although they wouldn't be exactly the same, since there are some
specific rules for "void" that don't apply to incomplete types in
general).  We then relaxed specific rules until the types were actually
useful.

Things in favour of (1)
-----------------------

The reasons above were mostly negative, arriving at (1) by elimination.
A more positive justification of this approach is that it 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 language extensions.  By default,
any language extension would treat the new types like other incomplete
types and choose conservatively-correct behaviour.  It would then be
possible to relax the language extension if this default behaviour
turns out to be too restrictive.

(That said, treating the types as permanently incomplete still won't
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.)


The SVE types in more detail
============================

Arm has published an SVE "ACLE" that specifies the SVE types and intrinsic
functions in detail.  For reference this is available without registration
at:

    https://static.docs.arm.com/100987/0000/acle_sve_100987_0000_00_en.pdf

but I'll try to keep this self-contained.

The ACLE defines a vector type sv<base>_t for each supported element type
<base>_t, so that the complete set is:

    svint8_t      svint16_t     svint32_t     svint64_t
    svuint8_t     svuint16_t    svuint32_t    svuint64_t
                  svfloat16_t   svfloat32_t   svfloat64_t

The types in each column have the same number of lanes and have twice
as many lanes as those in the column to the right.  Every vector has
the same number of bytes in total, with the number of bytes being
determined at runtime.

The ACLE also defines a single predicate type:

    svbool_t

that has the same number of lanes as svint8_t and svuint8_t.

All these types are opaque builtin types and are only expected to
be used with the associated ACLE intrinsics.  There are intrinsics for
creating vectors from scalars, loading from scalars, storing to scalars,
reinterpreting one type as another, etc.

The idea is that the vector types would only be used for short-term
register-sized working data.  Longer-term data would typically be stored
out to arrays.

For example, the vector function underlying:

    #pragma omp declare simd
    double sin(double);

would be:

    svfloat64_t mangled_sin(svfloat64_t, svbool_t);

(The svbool_t is because SVE functions should be predicated by default,
to avoid the need for a scalar epilogue loop.)

The ACLE also defines x2, x3 and x4 tuple types for each vector type;
for example, svint8x3_t is a tuple of 3 svint8_ts.  The tuples are
structure-like types with fields v0, v1, v2 and v3, up to the number
required.


Outline of the type system changes
==================================

Going back to the summary at the start of the RFC, C classifies types as
"complete" (the size of objects can be calculated) or "incomplete" (the
size of objects can't be calculated).  There's very little you can do
with a type until it becomes complete.

The approach we took was to treat all the SVE types as permanently
incomplete.  We then went through the standard relaxing specific
rules until the types were actually useful.

The first step was to classify types as:

  * "indefinite" (lacking sufficient information to create an object of
    that type) or "definite" (having sufficient information)

  * "sized" (will have a known size when definite) or "sizeless" (will
    never have a known 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 unchanged from the standard.
Thus a "definite type" is one that has been fully-defined rather than
simply declared, and "complete" is now equivalent to "sized and definite".
All standard types are "sized" (even "void", although it's always
indefinite and incomplete).

We then needed to make some rules use the distinction between "indefinite"
and "definite" rather than "incomplete" and "complete".  The specific
things we wanted to allow were:

  * defining automatic variables with sizeless definite type
  * defining functions whose parameters have sizeless definite type
  * defining functions that return a sizeless definite type
  * using sizeless definite types in _Generic associations
  * dereferencing pointers to sizeless definite types

Specific things we wanted to remain invalid -- by inheriting the rules from
incomplete types -- were:

  * creating or accessing arrays that have sizeless element types
  * doing pointer arithmetic on pointers to sizeless types
  * using sizeof and _Alignof with a sizeless type (or object of sizeless type)
  * defining (sized) unions or structures with sizeless members

It also seemed worth adding an extra restriction:

  * variables with sizeless type must not have static or thread-local
    storage duration

In practice it's impossible to define such variables with incomplete type,
but having an explicit rule means that things like:

    extern svint8_t foo;  // An SVE vector of int8_t elements.

are outright invalid rather than simply useless (because no other
translation unit could ever define foo).  Similarly, without an
explicit rule:

    svint8_t foo;         // An SVE vector of int8_t elements.

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.

This restriction isn't critical but it gives better diagnostics.


Sizeless structures (and testing on non-SVE targets)
====================================================

We're planning to build all SVE intrinsic types directly into GCC
(patches already written).  SVE therefore doesn't strictly need a syntax
for creating new sizeless types in C and C++.  However, having a way of
creating new structure-like "sizeless" types would be useful for three
reasons:

  - Functions could return arbitrary data by value.  The SVE ABI allows
    a function to return up to 8 vectors and 4 predicates in registers,
    which is far more flexible than the intrinsic types.

  - We could use these sizeless structure types to test the functionality
    on all targets.

  - A lot of the C++ frontend is concerned with classes, and having
    a way of creating sizeless classes would help make the C++ changes
    more consistent.

The patches therefore add a new "__sizeless_struct" keyword to denote
structures that are sizeless rather than sized.  Unlike normal
structures, these structures can have members of sizeless type in
addition to members of sized type.  On the other hand, they have all
the same limitations as other sizeless types (described in earlier
sections).

E.g., a sizeless structure definition might look like:

    __sizeless_struct data {
      double *array;
      svuint64_t indices;  // An SVE vector of uint64_t elements.
      svbool_t active;     // An SVE predicate.
    };

Adding a new keyword seemed better than using an attribute because it
means that the sized vs. sizeless distinction is fixed by the declaration.
E.g.:

    struct data;                     // Is it sized or sizeless?
    extern struct data global_data;  // OK if sized, not if sizeless.
    struct __attribute__((sizeless)) data {
      double *array;
      svuint64_t indices;            // An SVE vector of uint64_t elements.
      svbool_t active;               // An SVE predicate.
    };

would lead to the declaration of "global_data" sneaking through
despite being invalid when "data" is sizeless.

The tests in the patches all use these __sizeless_structs; they contain
nothing SVE- or AArch64-specific.


Other variable-length vector architectures
==========================================

The proposed RISC-V vector extension also has variable-length vectors.
When this language change was discussed on the clang developers' list,
Bruce Hoult (from SiFive, but speaking personally) replied with:

    http://lists.llvm.org/pipermail/cfe-dev/2018-May/057943.html

That message covers some of the background about the vector extension.
On the language changes, Bruce said:

    > However, even though the length is variable, the concept of a
    > "register-sized" C and C++ vector type makes just as much sense for SVE
    > as it does for other vector architectures.  Vector library functions
    > take such register-sized vectors as input and return them as results.
    > Intrinsic functions are also just as useful as they are for other vector
    > architectures, and they too take register-sized vectors as input and
    > return them as results.

    Intrinsic functions are absolutely required, and are I think the main
    reason for such a low-level register-sized vector type to exist.

[ Bruce went on to say:

    I'm not sure whether user-written functions operating on register-sized
    vectors are useful enough to support. User-written functions would normally
    take and return a higher-level vector type, and would implement the desired
    functionality in terms of calls to other user-written functions (operating
    on the high level vector as a whole) and/or explicit loops iterating
    through the high level vector type using intrinsic functions on the
    register-sized vector type proposed here.

But this use case is very important for SVE, since it will allow us
to implement vector math routines in a way that works with the OpenMP
"declare simd" construct.  There was also talk on gcc@ recently about
supporting this style of interface for RISC-V. ]

[...]

    > All these types are opaque builtin types and are only intended to be
    > used with the associated ACLE intrinsics.  There are intrinsics for
    > creating vectors from scalars, loading from scalars, storing to scalars,
    > reinterpreting one type as another, etc.
    >
    > The idea is that the vector types would only be used for short-term
    > register-sized working data.  Longer-term data would typically be stored
    > out to arrays.

    I agree with this.

[...]

    > The approach we took was to treat all the SVE types as permanently
    > incomplete.

    This seems reasonable.

So it looks like this extension would be useful for at least one
architecture besides SVE.


Edits to the C standard
=======================

This section specifies the behaviour for sizeless types as an edit to N1570.
There are three stages:

  - base changes, which add enough support for built-in sizeless
    vector types

  - updates for consistency, which change some of the wording without
    changing the meaning

  - support for sizeless structures

In each case, -strikethrough- indicates deleted text and *bold*
includes additional text.


Base changes
------------

These changes are enough to support sizeless built-in vector types.

    6.2.5 Types
    -----------

    1. The meaning of a value stored in an object or returned by a
    function is determined by the type of the expression used to access
    it. … Types are partitioned into object types (types that
    describe objects) and function types (types that describe
    functions).  -At various points within a translation unit an object
    type may be incomplete (lacking sufficient information to determine
    the size of objects of that type) or complete (having sufficient
    information).37)- *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.*

    1A. *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).37) 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.*

    1B. *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.*

    …

    19. The void type comprises an empty set of values; it is -an
    incomplete- *a sized indefinite* object type that cannot be completed
    *(made definite)*.

    …

    37) A type may be -incomplete- *indefinite* or -complete- *definite*
    throughout an entire translation unit, or it may change states at
    different points within a translation unit.

    …

    6.3.2.1 Lvalues, arrays, and function designators
    -------------------------------------------------

    1.  An lvalue is an expression (with an object type other than void)
    that potentially designates an object;64) … A modifiable lvalue is
    an lvalue that does not have array type, does not have an
    -incomplete- *indefinite* type, does not have a const-qualified
    type, …

    2.  Except when it is the operand of the sizeof operator, the
    _Alignof operator, the unary & operator, the ++ operator, the --
    operator, or the left operand of the . operator or an assignment
    operator, an lvalue that does not have array type is converted to
    the value stored in the designated object (and is no longer an
    lvalue); this is called lvalue conversion. … If the lvalue has an
    -incomplete- *indefinite* type and does not have array type, the
    behavior is undefined. …

    …

    6.5.1.1 Generic selection
    -------------------------

    …

    Constraints

    2. A generic selection shall have no more than one default generic
    association. The type name in a generic association shall specify a
    -complete- *definite* object type other than a variably modified
    type. …

    …

    6.5.2.2 Function calls
    ----------------------

    Constraints

    1. The expression that denotes the called function92) shall have
    type pointer to function returning void or returning a -complete-
    *definite* object type other than an array type.

    …

    Semantics

    …

    4. An argument may be an expression of any -complete- *definite* object
    type. …

    …

    6.5.2.5 Compound literals
    -------------------------

    Constraints

    1. The type name shall specify a -complete- *definite* object type or an
    array of unknown size, but not a variable length array type.

    …

    6.7 Declarations
    ----------------

    Constraints

    …

    4A. *If an identifier for an object does not have automatic storage
    duration, its type must be sized rather than sizeless.*

    Semantics

    …

    7. If an identifier for an object is declared with no linkage, the
    type for the object shall be -complete- *definite* by the end of its
    declarator, or by the end of its init-declarator if it has an
    initializer; in the case of function parameters (including in
    prototypes), it is the adjusted type (see 6.7.6.3) that is required
    to be -complete- *definite*.

    …
     
    6.7.6.3 Function declarators (including prototypes) 
    ---------------------------------------------------

    Constraints

    …

    4. After adjustment, the parameters in a parameter type list in a
    function declarator that is part of a definition of that function
    shall not have -incomplete- *indefinite* type.

    …

    6.7.9 Initialization
    --------------------

    Constraints

    …

    3. The type of the entity to be initialized shall be an array of
    unknown size or a -complete- *definite* object type that is not a
    variable length array type.

    …

    6.9.1 Function definitions
    --------------------------

    Constraints

    …

    3. The return type of a function shall be void or a -complete-
    *definite* object type other than array type.

    …

    Semantics

    …

    7. The declarator in a function definition specifies the name of the
    function being defined and the identifiers of its parameters. …
    [T]he type of each parameter is adjusted as described in
    6.7.6.3 for a parameter type list; the resulting type shall be a
    -complete- *definite* object type.

    …

    J.2 Undefined behavior
    ----------------------

        …
      * A non-array lvalue with -an incomplete- *an indefinite* type is used
        in a context that requires the value of the designated object
        (6.3.2.1).
        …
      * An identifier for an object is declared with no linkage and the
        type of the object is -incomplete- *indefinite* after its
        declarator, or after its init-declarator if it has an
        initializer (6.7).
        …
      * An adjusted parameter type in a function definition is not a
        -complete- *definite* object type (6.9.1).
        …

Updates for consistency
-----------------------

These changes are a prerequisite for sizeless structures.  They have no
effect otherwise, but might be preferred anyway because they make the
terminology more consistent.  They apply on top of the previous edits.

    6.2.5 Types
    -----------

    …

    22. An array type of unknown size is an -incomplete- *indefinite*
    type. It is -completed- *made definite*, for an identifier of that type,
    by specifying the size in a later declaration (with internal or
    external linkage). A structure or union type of unknown content (as
    described in 6.7.2.3) is an -incomplete- *indefinite* type. It is
    -completed- *made definite*, for all declarations of that type, by
    declaring the same structure or union tag with its defining content
    later in the same scope.

    …

    6.2.7 Compatible type and composite type
    ----------------------------------------

    1. Two types have compatible type if their types are the same. …
    Moreover, two structure, union, or enumerated types declared in
    separate translation units are compatible if their tags and members
    satisfy the following requirements: If one is declared with a tag,
    the other shall be declared with the same tag. If both are
    -completed- *made definite* anywhere within their respective
    translation units, then the following additional requirements apply: …

    …

    6.7.2.1 Structure and union specifiers
    --------------------------------------

    …

    Semantics

    …

    8. The presence of a struct-declaration-list in a
    struct-or-union-specifier declares a new type, within a translation
    unit. The struct-declaration-list is a sequence of declarations for
    the members of the structure or union.  If the struct-declaration-list
    does not contain any named members, either directly or via an anonymous
    structure or anonymous union, the behavior is undefined.  The type is
    -incomplete- *indefinite* until immediately after the } that terminates
    the list, and -complete- *definite* thereafter.

    …

    6.7.2.2 Enumeration specifiers
    ------------------------------

    …

    Semantics

    …

    4. … The enumerated type is -incomplete- *indefinite* until
    immediately after the } that terminates the list of enumerator
    declarations, and -complete- *definite* thereafter.

    …

    6.7.2.3 Tags
    ------------

    …

    Semantics

    4. All declarations of structure, union, or enumerated types that
    have the same scope and use the same tag declare the same
    type. Irrespective of whether there is a tag or what other
    declarations of the type are in the same translation unit, the type
    is -incomplete- *indefinite* 129) until immediately after the closing
    brace of the list defining the content, and -complete- *definite*
    thereafter.

    …

    8. If a type specifier of the form

    struct-or-union identifier

    occurs other than as part of one of the above forms, and no other
    declaration of the identifier as a tag is visible, then it declares
    an -incomplete- *indefinite* structure or union type, and declares the
    identifier as the tag of that type.131)

    …

    129) An -incomplete- *indefinite* type may only by used when -the
    size of an object- *the ability to create an object* of that type
    is not needed.  It is not needed, for example, when a typedef name
    is declared to be a specifier for a structure or union, or when a
    pointer to or a function returning a structure or union is being
    declared. (See -incomplete- *indefinite* types in 6.2.5.) The
    specification has to be -complete- *definite* before such a function
    is called or defined.

    6.7.6.3 Function declarators (including prototypes) 
    ---------------------------------------------------

    …

    Semantics

    …

    12.  If the function declarator is not part of a definition of that
    function, parameters may have -incomplete- *indefinite* type and may use
    the [*] notation in their sequences of declarator specifiers to
    specify variable length array types.

    …

    J.2 Undefined behavior
    ----------------------

        …
      * When the -complete- *definite* type is needed, an -incomplete-
        *indefinite* structure or union type is not completed in the same
        scope by another declaration of the tag that defines the content
        (6.7.2.3).
        …

Sizeless structures
-------------------

These additional changes to N1570 add the concept of a sizeless structure.
Again they apply on top of the edits above:

    6.2.3 Name spaces of identifiers
    --------------------------------

    1. If more than one declaration of a particular identifier is
    visible at any point in a translation unit, the syntactic context
    disambiguates uses that refer to different entities. Thus, there
    are separate name spaces for various categories of identifiers, as
    follows:

	…

      * the tags of *sized* structures, *sizeless structures,* unions, and
	enumerations (disambiguated by following any32) of the keywords
	struct, *__sizeless_struct,* union, or enum);

	…

    6.2.5 Types
    -----------

    1. … Types are partitioned into object types (types that describe
    objects) and function types (types that describe functions).
    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.-
    *the only sizeless types defined by this standard are __sizeless_structs,
    but an implementation may provide additional sizeless types.*

    …

    1B. 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.

    …

    20. Any number of derived types can be constructed from the object
    and function types, as follows: …

      * A *sized* structure type describes a sequentially allocated
        nonempty set of sized member objects (and, in certain
        circumstances, an incomplete array), each of which has an
        optionally specified name and possibly distinct type.

      * *A sizeless structure type describes a set of non-overlapping
        member objects whose types may be sizeless and whose relative
        positions are unspecified.  It is also unspecified whether the
        structure occupies a single contiguous piece of storage or
        whether it requires several disjoint pieces.*

    …

    *20A. The term structure type refers collectively to sized structure
    types and sizeless structure types.*

    …

    6.4.1 Keywords
    --------------

    Syntax

    1. *(Add __sizeless_struct to the list and update the copy in A.1.2)*

    …

    6.5.8 Relational operators
    --------------------------

    …

    Semantics

    …

    5. When two pointers are compared, the result depends on the
    relative locations in the address space of the objects pointed to.
    … If the objects pointed to are members of the same aggregate object,
    pointers to *sized* structure members declared later compare greater
    than pointers to members declared earlier in the structure, and
    pointers to array elements with larger subscript values compare
    greater than pointers to elements of the same array with lower
    subscript values. …

    …

    6.7.2.1 Structure and union specifiers
    --------------------------------------

    Syntax

    struct-or-union-specifier:
        struct-or-union identifieropt { struct-declaration-list }
        struct-or-union identifier

    struct-or-union:
        struct
        *__sizeless_struct*
        union

    …

    3. A *sized* structure or union shall not contain a member with
    incomplete or function type …, except that the last member of a
    structure with more than one named member may have incomplete array
    type; such a structure (and any union containing, possibly
    recursively, a member that is such a structure) shall not be a
    member of a structure or an element of an array.  *Simlarly, a
    sizeless structure shall not contain a member with indefinite or
    function type; the exception for incomplete array types does not
    apply.*

    …

    Semantics

    6. As discussed in 6.2.5, a *sized* structure is a type consisting
    of a sequence of members, whose storage is allocated in an ordered
    sequence; *a sizeless structure is a type consisting of
    non-overlapping members whose relative position is unspecified,*
    and a union is a type consisting of a sequence of members whose
    storage overlap.

    7. Structure and union specifiers have the same form. The keywords
    struct, *__sizeless_struct* and union indicate that the type being
    specified is, respectively, a *sized* structure type, *a sizeless
    structure type,* or a union type.

    …[8 is as above]…

    9. A member of a structure or union may have any complete object
    type other than a variably modified type.123)  *A member of a sizeless
    structure may also have a sizeless definite type.*  In addition, a
    member *of a structure or union* may be declared to consist of a
    specified number of bits (including a sign bit, if any). Such a
    member is called a bit-field;124) its width is preceded by a colon.

    …

    15. Within a *sized* structure object, the non-bit-field members and
    the units in which bit-fields reside have addresses that increase in
    the order in which they are declared. A pointer to a *sized* structure
    object, suitably converted, points to its initial member (or if that
    member is a bit-field, then to the unit in which it resides), and
    vice versa. There may be unnamed padding within a *sized* structure
    object, but not at its beginning.

    15A. *The representation of a sizeless structure object is
    unspecified.  It is possible to form pointers to the structure
    itself and to its individual members, but the relationship between
    their addresses is unspecified.  The structure may occupy a single
    piece of contiguous storage or it may occupy several disjoint
    pieces.*

    …

    18 As a special case, the last element of a *sized* structure with
    more than one named member may have an incomplete array type; this
    is called a flexible array member. …

    …

    6.7.2.3 Tags
    ------------

    Constraints

    …

    2. Where two declarations that use the same tag declare the same
    type, they shall both use the same choice of struct, *__sizeless_struct,*
    union, or enum.

    …


Edits to the C++ standard
=========================

We have a similar set of changes to the C++ standard, but this RFC is
long enough already, so I've not included them here.  I also didn't find
them to be particularly useful when writing the C++ patches, since most
of the changes were obvious given a few basic rules.  Those rules are:

  - type traits can be used with sizeless types (unlike incomplete types)

  - sizeless structures cannot have base classes or be used as base classes

  - sizeless structures cannot have virtual members

  - pointers to member variables are invalid for sizeless structures
    (although taking the address of a member of a specific sizeless object
    is fine, as for C)

  - sizeless types are not literal types

  - sizeless types cannot be created by operator new (as for incomplete types)

  - sizeless types cannot be deleted (so, unlike for incomplete types,
    this is an error rather than a warning)

  - sizeless types cannot be thrown or caught (as for incomplete types)

  - sizeless types cannot be used with typeid() (as for incomplete types)


GCC implementation questions
============================

The GCC patches are pretty simple in principle.  The language changes
involve going through the standard replacing "complete" with "definite"
and most of the GCC patches go through the frontend code making the
same kind of change.

New type flag for sizeless types
--------------------------------

The patches add a new flag TYPE_SIZELESS_P to represent the negative of:

  * would fully-defining the type determine its size?

from the summary above.  Negative names are usually a bad thing,
but the natural default is for the flag to be off.

There are currently 17 bits free in tree_type_common, so the patches
steal one of those.  Is that OK?

The effect on COMPLETE_TYPE_P
-----------------------------

The current definition of COMPLETE_TYPE_P is:

    /* Nonzero if this type is a complete type.  */
    #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)

Although the SVE types don't have a measurable size at the language
level, they still have a TYPE_SIZE and TYPE_SIZE_UNIT, with the sizes
using placeholders for the runtime vector size.  So after the split
described in the summary, TYPE_SIZE (NODE) != NULL_TREE means
"the type is fully defined" rather than "the type is complete".
With TYPE_SIZELESS_P, the definition of "complete type" would be:

    #define COMPLETE_TYPE_P(NODE) \
      (TYPE_SIZE (NODE) != NULL_TREE && !TYPE_SIZELESS_P (NODE))

i.e. the type is fully-defined, and fully-defining it determines
its size at the language level.

Uses of COMPLETE_TYPE_P outside the frontends
---------------------------------------------

The main complication is that the concept of "complete type" is exposed
outside the frontends, with COMPLETE_TYPE_P being defined in tree.h.

I tried to audit all uses outside the frontends and it looks like
they're all testing whether "the type is fully defined" and don't
care about the distinction between sized and sizeless.  This means
that the current definition (rather than the new definition)
should be correct in all cases.

In some cases the tests are simple null checks, like:

     /* Try to approach equal type sizes.  */
     if (!COMPLETE_TYPE_P (type_a)
         || !COMPLETE_TYPE_P (type_b)
         || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_a))
         || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_b)))
       break;

IMO it's more obvious to test TYPE_SIZE_UNIT directly for null here.
Having a wrapper doesn't add much.

In places like:

  if (!COMPLETE_TYPE_P (t))
    layout_type (t);

and:

  if (COMPLETE_TYPE_P (t) && TYPE_CANONICAL (t)
      && TYPE_MODE (t) != TYPE_MODE (TYPE_CANONICAL (t)))
    ...

it's testing whether the type has been laid out already.

So the patches do two things:

  * Expand the definition of the current COMPLETE_TYPE_P macro outside
    the frontends if the macro is simply protecting against a null
    dereference.

  * Make COMPLETE_TYPE_P local to the frontends and rename all uses
    outside the frontends.

As far as the second point goes, I wasn't sure what new name to use
outside the front ends.  Possibilities include:

  - DEFINITE_TYPE_P
  - USABLE_TYPE_P
  - VALID_VAR_TYPE_P
  - TYPE_LAID_OUT_P
  - TYPE_DEFINED_P
  - TYPE_FULLY_DEFINED_P
  - TYPE_READY_P
  ...other suggestions welcome...

I went for DEFINITE_TYPE_P because that's what the SVE specification
uses, but something more neutral like TYPE_DEFINED_P might be better.

Frontend changes
----------------

The frontend patches change COMPLETE_TYPE_P to DEFINITE_TYPE_P where
necessary.  I've tried where possible to accompany each individual
change with a test.

This worked fairly naturally (IMO) for C, and most of the changes could
be tied directly to the language edits above.

For C++ it was more difficult (not surprisingly).  There are a lot of
tests for COMPLETE_TYPE_P that are obviously testing whether a class
has been fully defined, and are more concerned with name lookup than
TYPE_SIZE.  The same goes for COMPLETE_OR_OPEN_TYPE_P and whether the
definition has been started.  So while the C changes were relatively
small and self-contained, the C++ changes replace many more uses of
COMPLETE_TYPE_P than they keep.  This makes me wonder whether it's a
good idea to keep COMPLETE_TYPE_P at all, or whether it would be better
to replace the remaining uses with something more explicit like:

  TYPE_SIZE_KNOWN_P
  TYPE_SIZE_DEFINED_P
  TYPE_SIZE_MEASURABLE_P
  TYPE_SIZE_COMPLETE_P
  ...suggestions again welcome...

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [01/10] Expand COMPLETE_TYPE_P in obvious checks for null
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
@ 2018-10-15 14:33 ` Richard Sandiford
  2018-10-18 19:32   ` Jeff Law
  2018-10-15 14:34 ` [03/10] Move COMPLETE_OR_VOID_TYPE_P to the C and C++ frontends Richard Sandiford
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

Some tests for COMPLETE_TYPE_P are just protecting against a null
TYPE_SIZE or TYPE_SIZE_UNIT.  Rather than replace them with a new
macro, it seemed clearer to write out the underlying test.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* calls.c (initialize_argument_information): Replace COMPLETE_TYPE_P
	with checks for null.
	* config/aarch64/aarch64.c (aapcs_vfp_sub_candidate): Likewise.
	* config/arm/arm.c (aapcs_vfp_sub_candidate): Likewise.
	* config/powerpcspe/powerpcspe.c (rs6000_aggregate_candidate):
	Likewise.
	* config/riscv/riscv.c (riscv_flatten_aggregate_field): Likewise.
	* config/rs6000/rs6000.c (rs6000_aggregate_candidate): Likewise.
	* expr.c (expand_assignment, safe_from_p): Likewise.
	(expand_expr_real_1): Likewise.
	* tree-data-ref.c (initialize_data_dependence_relation): Likewise.
	* tree-sra.c (maybe_add_sra_candidate): Likewise.
	(find_param_candidates): Likewise.
	* tree-ssa-alias.c (indirect_ref_may_alias_decl_p): Likewise.
	* tree-vrp.c (vrp_prop::check_mem_ref): Likewise.

gcc/lto/
	* lto-symtab.c (warn_type_compatibility_p): Likewise.

Index: gcc/calls.c
===================================================================
--- gcc/calls.c	2018-10-05 13:46:11.115788209 +0100
+++ gcc/calls.c	2018-10-15 14:12:54.016553288 +0100
@@ -2039,7 +2039,7 @@ initialize_argument_information (int num
 		 function being called.  */
 	      rtx copy;
 
-	      if (!COMPLETE_TYPE_P (type)
+	      if (!TYPE_SIZE_UNIT (type)
 		  || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
 		  || (flag_stack_check == GENERIC_STACK_CHECK
 		      && compare_tree_int (TYPE_SIZE_UNIT (type),
Index: gcc/config/aarch64/aarch64.c
===================================================================
--- gcc/config/aarch64/aarch64.c	2018-10-15 14:08:45.970608817 +0100
+++ gcc/config/aarch64/aarch64.c	2018-10-15 14:12:54.020553256 +0100
@@ -13000,7 +13000,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -13033,7 +13033,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -13066,7 +13066,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
Index: gcc/config/arm/arm.c
===================================================================
--- gcc/config/arm/arm.c	2018-10-05 13:46:14.375761802 +0100
+++ gcc/config/arm/arm.c	2018-10-15 14:12:54.024553222 +0100
@@ -5927,7 +5927,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -5960,7 +5960,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -5993,7 +5993,7 @@ aapcs_vfp_sub_candidate (const_tree type
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
Index: gcc/config/powerpcspe/powerpcspe.c
===================================================================
--- gcc/config/powerpcspe/powerpcspe.c	2018-10-05 13:46:13.855766014 +0100
+++ gcc/config/powerpcspe/powerpcspe.c	2018-10-15 14:12:54.032553156 +0100
@@ -11540,7 +11540,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -11573,7 +11573,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -11606,7 +11606,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
Index: gcc/config/riscv/riscv.c
===================================================================
--- gcc/config/riscv/riscv.c	2018-10-05 13:46:13.879765820 +0100
+++ gcc/config/riscv/riscv.c	2018-10-15 14:12:54.032553156 +0100
@@ -2290,7 +2290,7 @@ riscv_flatten_aggregate_field (const_tre
     {
     case RECORD_TYPE:
      /* Can't handle incomplete types nor sizes that are not fixed.  */
-     if (!COMPLETE_TYPE_P (type)
+     if (!TYPE_SIZE (type)
 	 || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
 	 || !tree_fits_uhwi_p (TYPE_SIZE (type)))
        return -1;
@@ -2319,7 +2319,7 @@ riscv_flatten_aggregate_field (const_tre
 
 	/* Can't handle incomplete types nor sizes that are not fixed.  */
 	if (n_subfields <= 0
-	    || !COMPLETE_TYPE_P (type)
+	    || !TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
 	    || !index
 	    || !TYPE_MAX_VALUE (index)
Index: gcc/config/rs6000/rs6000.c
===================================================================
--- gcc/config/rs6000/rs6000.c	2018-10-15 14:08:45.994608617 +0100
+++ gcc/config/rs6000/rs6000.c	2018-10-15 14:12:54.040553089 +0100
@@ -10386,7 +10386,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -10419,7 +10419,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
@@ -10452,7 +10452,7 @@ rs6000_aggregate_candidate (const_tree t
 
 	/* Can't handle incomplete types nor sizes that are not
 	   fixed.  */
-	if (!COMPLETE_TYPE_P (type)
+	if (!TYPE_SIZE (type)
 	    || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 	  return -1;
 
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2018-10-05 13:46:10.003797217 +0100
+++ gcc/expr.c	2018-10-15 14:12:54.040553089 +0100
@@ -5300,7 +5300,7 @@ expand_assignment (tree to, tree from, b
      needs to be done.  Handling this in the normal way is safe because no
      computation is done before the call.  The same is true for SSA names.  */
   if (TREE_CODE (from) == CALL_EXPR && ! aggregate_value_p (from, from)
-      && COMPLETE_TYPE_P (TREE_TYPE (from))
+      && TYPE_SIZE (TREE_TYPE (from))
       && TREE_CODE (TYPE_SIZE (TREE_TYPE (from))) == INTEGER_CST
       && ! (((VAR_P (to)
 	      || TREE_CODE (to) == PARM_DECL
@@ -7518,7 +7518,9 @@ safe_from_p (const_rtx x, tree exp, int
 	 So we assume here that something at a higher level has prevented a
 	 clash.  This is somewhat bogus, but the best we can do.  Only
 	 do this when X is BLKmode and when we are at the top level.  */
-      || (top_p && TREE_TYPE (exp) != 0 && COMPLETE_TYPE_P (TREE_TYPE (exp))
+      || (top_p
+	  && TREE_TYPE (exp) != 0
+	  && TYPE_SIZE (TREE_TYPE (exp))
 	  && TREE_CODE (TYPE_SIZE (TREE_TYPE (exp))) != INTEGER_CST
 	  && (TREE_CODE (TREE_TYPE (exp)) != ARRAY_TYPE
 	      || TYPE_ARRAY_MAX_SIZE (TREE_TYPE (exp)) == NULL_TREE
@@ -10523,7 +10525,7 @@ expand_expr_real_1 (tree exp, rtx target
 	orig_op0 = op0
 	  = expand_expr_real (tem,
 			      (TREE_CODE (TREE_TYPE (tem)) == UNION_TYPE
-			       && COMPLETE_TYPE_P (TREE_TYPE (tem))
+			       && TYPE_SIZE (TREE_TYPE (tem))
 			       && (TREE_CODE (TYPE_SIZE (TREE_TYPE (tem)))
 				   != INTEGER_CST)
 			       && modifier != EXPAND_STACK_PARM
Index: gcc/tree-data-ref.c
===================================================================
--- gcc/tree-data-ref.c	2018-10-05 13:46:11.115788209 +0100
+++ gcc/tree-data-ref.c	2018-10-15 14:12:54.040553089 +0100
@@ -2487,8 +2487,8 @@ initialize_data_dependence_relation (str
 	}
 
       /* Try to approach equal type sizes.  */
-      if (!COMPLETE_TYPE_P (type_a)
-	  || !COMPLETE_TYPE_P (type_b)
+      if (!TYPE_SIZE_UNIT (type_a)
+	  || !TYPE_SIZE_UNIT (type_b)
 	  || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_a))
 	  || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_b)))
 	break;
Index: gcc/tree-sra.c
===================================================================
--- gcc/tree-sra.c	2018-08-28 11:25:46.034881666 +0100
+++ gcc/tree-sra.c	2018-10-15 14:12:54.040553089 +0100
@@ -1988,9 +1988,9 @@ maybe_add_sra_candidate (tree var)
       reject (var, "is volatile");
       return false;
     }
-  if (!COMPLETE_TYPE_P (type))
+  if (!TYPE_SIZE (type))
     {
-      reject (var, "has incomplete type");
+      reject (var, "type size unknown");
       return false;
     }
   if (!tree_fits_uhwi_p (TYPE_SIZE (type)))
@@ -4176,7 +4176,7 @@ find_param_candidates (void)
       else if (!AGGREGATE_TYPE_P (type))
 	continue;
 
-      if (!COMPLETE_TYPE_P (type)
+      if (!TYPE_SIZE (type)
 	  || !tree_fits_uhwi_p (TYPE_SIZE (type))
           || tree_to_uhwi (TYPE_SIZE (type)) == 0
 	  || (AGGREGATE_TYPE_P (type)
Index: gcc/tree-ssa-alias.c
===================================================================
--- gcc/tree-ssa-alias.c	2018-09-10 17:38:19.470814110 +0100
+++ gcc/tree-ssa-alias.c	2018-10-15 14:12:54.040553089 +0100
@@ -1182,7 +1182,8 @@ indirect_ref_may_alias_decl_p (tree ref1
   /* If the size of the access relevant for TBAA through the pointer
      is bigger than the size of the decl we can't possibly access the
      decl via that pointer.  */
-  if (DECL_SIZE (base2) && COMPLETE_TYPE_P (TREE_TYPE (ptrtype1))
+  if (DECL_SIZE (base2)
+      && TYPE_SIZE (TREE_TYPE (ptrtype1))
       && poly_int_tree_p (DECL_SIZE (base2))
       && poly_int_tree_p (TYPE_SIZE (TREE_TYPE (ptrtype1)))
       /* ???  This in turn may run afoul when a decl of type T which is
Index: gcc/tree-vrp.c
===================================================================
--- gcc/tree-vrp.c	2018-10-05 13:46:08.215811700 +0100
+++ gcc/tree-vrp.c	2018-10-15 14:12:54.044553056 +0100
@@ -4412,7 +4412,7 @@ vrp_prop::check_mem_ref (location_t loca
      not known.  */
   tree reftype = TREE_TYPE (arg);
   if (POINTER_TYPE_P (reftype)
-      || !COMPLETE_TYPE_P (reftype)
+      || !TYPE_SIZE_UNIT (reftype)
       || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST
       || RECORD_OR_UNION_TYPE_P (reftype))
     return;
Index: gcc/lto/lto-symtab.c
===================================================================
--- gcc/lto/lto-symtab.c	2018-08-28 11:25:46.034881666 +0100
+++ gcc/lto/lto-symtab.c	2018-10-15 14:12:54.040553089 +0100
@@ -243,8 +243,8 @@ warn_type_compatibility_p (tree prevaili
   /* We can not use types_compatible_p because we permit some changes
      across types.  For example unsigned size_t and "signed size_t" may be
      compatible when merging C and Fortran types.  */
-  if (COMPLETE_TYPE_P (prevailing_type)
-      && COMPLETE_TYPE_P (type)
+  if (TYPE_SIZE (type)
+      && TYPE_SIZE (prevailing_type)
       /* While global declarations are never variadic, we can recurse here
 	 for function parameter types.  */
       && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [03/10] Move COMPLETE_OR_VOID_TYPE_P to the C and C++ frontends
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
  2018-10-15 14:33 ` [01/10] Expand COMPLETE_TYPE_P in obvious checks for null Richard Sandiford
@ 2018-10-15 14:34 ` Richard Sandiford
  2018-10-15 14:34 ` [02/10] Replace most uses of COMPLETE_TYPE_P outside the frontends Richard Sandiford
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

There was only one use of this macro outside the frontends, in dbxout.c.
This patch expands that use and moves the macro's definition to c-common.h.

There's no expectation that dbx will support sizeless types,
so keeping the current definition should be fine.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree.h (COMPLETE_OR_VOID_TYPE_P): Move to c-common.h.
	* dbxout.c (dbxout_typedefs): Expand definition of
	COMPLETE_OR_VOID_TYPE_P.

gcc/c-family/
	* c-common.h (COMPLETE_OR_VOID_TYPE_P): Moved from tree.h.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-15 14:12:59.036511679 +0100
+++ gcc/tree.h	2018-10-15 14:13:04.148469305 +0100
@@ -602,10 +602,6 @@ #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE
 /* Nonzero if this type is the (possibly qualified) void type.  */
 #define VOID_TYPE_P(NODE) (TREE_CODE (NODE) == VOID_TYPE)
 
-/* Nonzero if this type is complete or is cv void.  */
-#define COMPLETE_OR_VOID_TYPE_P(NODE) \
-  (COMPLETE_TYPE_P (NODE) || VOID_TYPE_P (NODE))
-
 /* Nonzero if this type is complete or is an array with unspecified bound.  */
 #define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
   (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
Index: gcc/dbxout.c
===================================================================
--- gcc/dbxout.c	2018-10-15 14:12:59.024511777 +0100
+++ gcc/dbxout.c	2018-10-15 14:13:04.148469305 +0100
@@ -1093,7 +1093,7 @@ dbxout_typedefs (tree syms)
 	  tree type = TREE_TYPE (syms);
 	  if (TYPE_NAME (type)
 	      && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
-	      && COMPLETE_OR_VOID_TYPE_P (type)
+	      && (DEFINITE_TYPE_P (type) || VOID_TYPE_P (type))
 	      && ! TREE_ASM_WRITTEN (TYPE_NAME (type)))
 	    dbxout_symbol (TYPE_NAME (type), 0);
 	}
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:08:44.946617301 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:04.148469305 +0100
@@ -742,6 +742,10 @@ #define C_TYPE_FUNCTION_P(type) \
 #define C_TYPE_OBJECT_OR_INCOMPLETE_P(type) \
   (!C_TYPE_FUNCTION_P (type))
 
+/* Nonzero if this type is complete or is cv void.  */
+#define COMPLETE_OR_VOID_TYPE_P(NODE) \
+  (COMPLETE_TYPE_P (NODE) || VOID_TYPE_P (NODE))
+
 struct visibility_flags
 {
   unsigned inpragma : 1;	/* True when in #pragma GCC visibility.  */

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [02/10] Replace most uses of COMPLETE_TYPE_P outside the frontends
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
  2018-10-15 14:33 ` [01/10] Expand COMPLETE_TYPE_P in obvious checks for null Richard Sandiford
  2018-10-15 14:34 ` [03/10] Move COMPLETE_OR_VOID_TYPE_P to the C and C++ frontends Richard Sandiford
@ 2018-10-15 14:34 ` Richard Sandiford
  2018-10-15 14:35 ` [04/10] Move COMPLETE_OR_UNBOUND_ARRAY_TYPE_P to the C and C++ frontends Richard Sandiford
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

This patch adds a DEFINITE_TYPE_P macro for testing whether a
type has been fully-defined.  The definition is the same as the
current definition of COMPLETE_TYPE_P, but later patches redefine
COMPLETE_TYPE_P and make it local to the C and C++ frontends.
The name "definite type" comes from the SVE ACLE specification.

The patch also replaces all *.c uses of COMPLETE_TYPE_P outside
the frontends (along with a couple of *.h uses).

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree.h (DEFINITE_TYPE_P): New macro.
	(type_with_alias_set_p): Use it instead of COMPLETE_TYPE_P.
	* alias.c (get_alias_set): Likewise.
	* calls.c (initialize_argument_information): Likewise.
	* config/i386/winnt.c (gen_stdcall_or_fastcall_suffix): Likewise.
	* convert.c (convert_to_integer_1): Likewise.
	* dbxout.c (dbxout_type, dbxout_symbol): Likewise.
	* dwarf2out.c (add_pubtype, gen_generic_params_dies)
	(add_subscript_info, gen_scheduled_generic_parms_dies): Likewise.
	* function.c (assign_temp): Likewise.
	* gimple-expr.c (create_tmp_var): Likewise.
	* gimplify.c (gimplify_expr): Likewise.
	* ipa-devirt.c (set_type_binfo, warn_types_mismatch)
	(odr_types_equivalent_p, add_type_duplicate, get_odr_type): Likewise.
	* ipa-icf.c (sem_item::add_type): Likewise.
	* langhooks.c (lhd_omp_mappable_type): Likewise.
	* omp-low.c (scan_sharing_clauses): Likewise.
	* tree-ssa-sccvn.c (fully_constant_vn_reference_p): Likewise.
	* tree.c (type_cache_hasher::equal, build_function_type)
	(build_method_type_directly, build_offset_type, build_complex_type)
	(verify_type_variant, gimple_canonical_types_compatible_p)
	(verify_type): Likewise.

gcc/ada/
	* gcc-interface/decl.c (gnat_to_gnu_entity): Likewise.
	* gcc-interface/utils2.c (build_simple_component_ref): Likewise.

gcc/fortran/
	* trans-decl.c (gfc_build_qualified_array): Likewise.
	(create_function_arglist): Likewise.

gcc/lto/
	* lto-symtab.c (lto_symtab_merge_decls_1): Likewise.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-05 13:46:08.863806452 +0100
+++ gcc/tree.h	2018-10-15 14:12:59.036511679 +0100
@@ -592,6 +592,10 @@ #define POINTER_TYPE_P(TYPE) \
 #define FUNCTION_POINTER_TYPE_P(TYPE) \
   (POINTER_TYPE_P (TYPE) && TREE_CODE (TREE_TYPE (TYPE)) == FUNCTION_TYPE)
 
+/* Nonzero if this type is "definite"; that is, if we have enough information
+   to create objects of that type.  The type might be sized or sizeless.  */
+#define DEFINITE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
+
 /* Nonzero if this type is a complete type.  */
 #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
 
@@ -5796,12 +5800,12 @@ type_with_alias_set_p (const_tree t)
   if (TREE_CODE (t) == FUNCTION_TYPE || TREE_CODE (t) == METHOD_TYPE)
     return false;
 
-  if (COMPLETE_TYPE_P (t))
+  if (DEFINITE_TYPE_P (t))
     return true;
 
   /* Incomplete types can not be accessed in general except for arrays
      where we can fetch its element despite we have no array bounds.  */
-  if (TREE_CODE (t) == ARRAY_TYPE && COMPLETE_TYPE_P (TREE_TYPE (t)))
+  if (TREE_CODE (t) == ARRAY_TYPE && DEFINITE_TYPE_P (TREE_TYPE (t)))
     return true;
 
   return false;
Index: gcc/alias.c
===================================================================
--- gcc/alias.c	2018-10-05 13:46:11.111788242 +0100
+++ gcc/alias.c	2018-10-15 14:12:59.020511811 +0100
@@ -922,15 +922,15 @@ get_alias_set (tree t)
   if (TYPE_ALIAS_SET_KNOWN_P (t))
     return TYPE_ALIAS_SET (t);
 
-  /* We don't want to set TYPE_ALIAS_SET for incomplete types.  */
-  if (!COMPLETE_TYPE_P (t))
+  /* We don't want to set TYPE_ALIAS_SET for indefinite types.  */
+  if (!DEFINITE_TYPE_P (t))
     {
       /* For arrays with unknown size the conservative answer is the
 	 alias set of the element type.  */
       if (TREE_CODE (t) == ARRAY_TYPE)
 	return get_alias_set (TREE_TYPE (t));
 
-      /* But return zero as a conservative answer for incomplete types.  */
+      /* But return zero as a conservative answer for indefinite types.  */
       return 0;
     }
 
@@ -1006,7 +1006,7 @@ get_alias_set (tree t)
       for (p = t; POINTER_TYPE_P (p)
 	   || (TREE_CODE (p) == ARRAY_TYPE
 	       && (!TYPE_NONALIASED_COMPONENT (p)
-		   || !COMPLETE_TYPE_P (p)
+		   || !DEFINITE_TYPE_P (p)
 		   || TYPE_STRUCTURAL_EQUALITY_P (p)))
 	   || TREE_CODE (p) == VECTOR_TYPE;
 	   p = TREE_TYPE (p))
Index: gcc/calls.c
===================================================================
--- gcc/calls.c	2018-10-15 14:12:54.016553288 +0100
+++ gcc/calls.c	2018-10-15 14:12:59.024511777 +0100
@@ -1950,7 +1950,7 @@ initialize_argument_information (int num
       machine_mode mode;
 
       /* Replace erroneous argument with constant zero.  */
-      if (type == error_mark_node || !COMPLETE_TYPE_P (type))
+      if (type == error_mark_node || !DEFINITE_TYPE_P (type))
 	args[i].tree_value = integer_zero_node, type = integer_type_node;
 
       /* If TYPE is a transparent union or record, pass things the way
Index: gcc/config/i386/winnt.c
===================================================================
--- gcc/config/i386/winnt.c	2018-06-14 12:27:39.888033693 +0100
+++ gcc/config/i386/winnt.c	2018-10-15 14:12:59.024511777 +0100
@@ -200,7 +200,7 @@ gen_stdcall_or_fastcall_suffix (tree dec
 	  HOST_WIDE_INT parm_size;
 	  HOST_WIDE_INT parm_boundary_bytes = PARM_BOUNDARY / BITS_PER_UNIT;
 
-	  if (! COMPLETE_TYPE_P (arg))
+	  if (! DEFINITE_TYPE_P (arg))
 	    break;
 
 	  parm_size = int_size_in_bytes (arg);
Index: gcc/convert.c
===================================================================
--- gcc/convert.c	2018-05-02 08:38:19.345317649 +0100
+++ gcc/convert.c	2018-10-15 14:12:59.024511777 +0100
@@ -521,9 +521,9 @@ convert_to_integer_1 (tree type, tree ex
   unsigned int outprec = element_precision (type);
   location_t loc = EXPR_LOCATION (expr);
 
-  /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
-     be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
-  if (!COMPLETE_TYPE_P (type))
+  /* An INTEGER_TYPE cannot be indefinite (incomplete), but an
+     ENUMERAL_TYPE can be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
+  if (!DEFINITE_TYPE_P (type))
     {
       error ("conversion to incomplete type");
       return error_mark_node;
Index: gcc/dbxout.c
===================================================================
--- gcc/dbxout.c	2018-10-05 13:46:09.999797249 +0100
+++ gcc/dbxout.c	2018-10-15 14:12:59.024511777 +0100
@@ -1881,7 +1881,7 @@ dbxout_type (tree type, int full)
 	 and either that's all we want or that's the best we could do,
 	 don't repeat the cross reference.
 	 Sun dbx crashes if we do.  */
-      if (! full || !COMPLETE_TYPE_P (type)
+      if (! full || !DEFINITE_TYPE_P (type)
 	  /* No way in DBX fmt to describe a variable size.  */
 	  || ! tree_fits_uhwi_p (TYPE_SIZE (type)))
 	return;
@@ -1906,7 +1906,7 @@ dbxout_type (tree type, int full)
 	 && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
 	       && DECL_IGNORED_P (TYPE_NAME (type)))
 	 && !full)
-	|| !COMPLETE_TYPE_P (type)
+	|| !DEFINITE_TYPE_P (type)
 	/* No way in DBX fmt to describe a variable size.  */
 	|| ! tree_fits_uhwi_p (TYPE_SIZE (type)))
       {
@@ -2164,7 +2164,7 @@ dbxout_type (tree type, int full)
 	     && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
 		   && DECL_IGNORED_P (TYPE_NAME (type)))
 	     && !full)
-	    || !COMPLETE_TYPE_P (type)
+	    || !DEFINITE_TYPE_P (type)
 	    /* No way in DBX fmt to describe a variable size.  */
 	    || ! tree_fits_uhwi_p (TYPE_SIZE (type)))
 	  {
@@ -2289,7 +2289,7 @@ dbxout_type (tree type, int full)
 	   && ! (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
 		 && DECL_IGNORED_P (TYPE_NAME (type)))
 	   && !full)
-	  || !COMPLETE_TYPE_P (type))
+	  || !DEFINITE_TYPE_P (type))
 	{
 	  stabstr_S ("xe");
 	  dbxout_type_name (type);
@@ -2815,7 +2815,7 @@ dbxout_symbol (tree decl, int local ATTR
 		   from explicit ones that might be found in C.  */
 		&& DECL_ARTIFICIAL (decl)
                 /* Do not generate a tag for incomplete records.  */
-                && COMPLETE_TYPE_P (type)
+                && DEFINITE_TYPE_P (type)
 		/* Do not generate a tag for records of variable size,
 		   since this type can not be properly described in the
 		   DBX format, and it confuses some tools such as objdump.  */
@@ -2864,13 +2864,13 @@ dbxout_symbol (tree decl, int local ATTR
 	    did_output = 1;
 	  }
 
-	/* Don't output a tag if this is an incomplete type.  This prevents
-	   the sun4 Sun OS 4.x dbx from crashing.  */
+	/* Don't output a tag if this is an indefinite (incomplete) type.
+	   This prevents the sun4 Sun OS 4.x dbx from crashing.  */
 
 	if (tag_needed && TYPE_NAME (type) != 0
 	    && (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE
 		|| (DECL_NAME (TYPE_NAME (type)) != 0))
-	    && COMPLETE_TYPE_P (type)
+	    && DEFINITE_TYPE_P (type)
 	    && !TREE_ASM_WRITTEN (TYPE_NAME (type)))
 	  {
 	    /* For a TYPE_DECL with no name, but the type has a name,
Index: gcc/dwarf2out.c
===================================================================
--- gcc/dwarf2out.c	2018-10-05 13:46:10.003797217 +0100
+++ gcc/dwarf2out.c	2018-10-15 14:12:59.028511745 +0100
@@ -11306,7 +11306,7 @@ add_pubtype (tree decl, dw_die_ref die)
 
   if ((TREE_PUBLIC (decl)
        || is_cu_die (die->die_parent) || is_namespace_die (die->die_parent))
-      && (die->die_tag == DW_TAG_typedef || COMPLETE_TYPE_P (decl)))
+      && (die->die_tag == DW_TAG_typedef || DEFINITE_TYPE_P (decl)))
     {
       tree scope = NULL;
       const char *scope_name = "";
@@ -13448,7 +13448,7 @@ gen_generic_params_dies (tree t)
   dw_die_ref die = NULL;
   int non_default;
 
-  if (!t || (TYPE_P (t) && !COMPLETE_TYPE_P (t)))
+  if (!t || (TYPE_P (t) && !DEFINITE_TYPE_P (t)))
     return;
 
   if (TYPE_P (t))
@@ -20924,7 +20924,7 @@ add_subscript_info (dw_die_ref type_die,
 	    {
 	      if (upper)
 		add_bound_info (subrange_die, DW_AT_upper_bound, upper, NULL);
-	      else if ((is_c () || is_cxx ()) && COMPLETE_TYPE_P (type))
+	      else if ((is_c () || is_cxx ()) && DEFINITE_TYPE_P (type))
 		/* Zero-length array.  */
 		add_bound_info (subrange_die, DW_AT_count,
 				build_int_cst (TREE_TYPE (lower), 0), NULL);
@@ -27011,7 +27011,7 @@ gen_scheduled_generic_parms_dies (void)
     return;
   
   FOR_EACH_VEC_ELT (*generic_type_instances, i, t)
-    if (COMPLETE_TYPE_P (t))
+    if (DEFINITE_TYPE_P (t))
       gen_generic_params_dies (t);
 
   generic_type_instances = NULL;
Index: gcc/function.c
===================================================================
--- gcc/function.c	2018-08-28 11:25:46.018881803 +0100
+++ gcc/function.c	2018-10-15 14:12:59.028511745 +0100
@@ -979,7 +979,7 @@ assign_temp (tree type_or_decl, int memo
 
   /* Allocating temporaries of TREE_ADDRESSABLE type must be done in the front
      end.  See also create_tmp_var for the gimplification-time check.  */
-  gcc_assert (!TREE_ADDRESSABLE (type) && COMPLETE_TYPE_P (type));
+  gcc_assert (!TREE_ADDRESSABLE (type) && DEFINITE_TYPE_P (type));
 
   if (mode == BLKmode || memory_required)
     {
Index: gcc/gimple-expr.c
===================================================================
--- gcc/gimple-expr.c	2018-05-02 08:38:14.433364094 +0100
+++ gcc/gimple-expr.c	2018-10-15 14:12:59.028511745 +0100
@@ -476,7 +476,7 @@ create_tmp_var (tree type, const char *p
      The processing for variable sizes is performed in gimple_add_tmp_var,
      point at which it really matters and possibly reached via paths not going
      through this function, e.g. after direct calls to create_tmp_var_raw.  */
-  gcc_assert (!TREE_ADDRESSABLE (type) && COMPLETE_TYPE_P (type));
+  gcc_assert (!TREE_ADDRESSABLE (type) && DEFINITE_TYPE_P (type));
 
   tmp_var = create_tmp_var_raw (type, prefix);
   gimple_add_tmp_var (tmp_var);
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	2018-09-25 08:03:44.415261520 +0100
+++ gcc/gimplify.c	2018-10-15 14:12:59.032511711 +0100
@@ -12421,7 +12421,7 @@ gimplify_expr (tree *expr_p, gimple_seq
 
 	  *expr_p = NULL;
 	}
-      else if (COMPLETE_TYPE_P (TREE_TYPE (*expr_p))
+      else if (DEFINITE_TYPE_P (TREE_TYPE (*expr_p))
 	       && TYPE_MODE (TREE_TYPE (*expr_p)) != BLKmode)
 	{
 	  /* Historically, the compiler has treated a bare reference
Index: gcc/ipa-devirt.c
===================================================================
--- gcc/ipa-devirt.c	2018-08-21 14:47:07.791167876 +0100
+++ gcc/ipa-devirt.c	2018-10-15 14:12:59.032511711 +0100
@@ -650,7 +650,7 @@ #define odr_types (*odr_types_ptr)
 set_type_binfo (tree type, tree binfo)
 {
   for (; type; type = TYPE_NEXT_VARIANT (type))
-    if (COMPLETE_TYPE_P (type))
+    if (DEFINITE_TYPE_P (type))
       TYPE_BINFO (type) = binfo;
     else
       gcc_assert (!TYPE_BINFO (type));
@@ -1171,7 +1171,8 @@ warn_types_mismatch (tree t1, tree t2, l
       if (TREE_CODE (t1) == TREE_CODE (t2))
 	{
 	  if (TREE_CODE (t1) == ARRAY_TYPE
-	      && COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2))
+	      && DEFINITE_TYPE_P (t1)
+	      && DEFINITE_TYPE_P (t2))
 	    {
 	      tree i1 = TYPE_DOMAIN (t1);
 	      tree i2 = TYPE_DOMAIN (t2);
@@ -1513,7 +1514,7 @@ odr_types_equivalent_p (tree t1, tree t2
 	tree f1, f2;
 
 	/* For aggregate types, all the fields must be the same.  */
-	if (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2))
+	if (DEFINITE_TYPE_P (t1) && DEFINITE_TYPE_P (t2))
 	  {
 	    if (TYPE_BINFO (t1) && TYPE_BINFO (t2)
 	        && polymorphic_type_binfo_p (TYPE_BINFO (t1))
@@ -1641,7 +1642,8 @@ odr_types_equivalent_p (tree t1, tree t2
 		   "is defined in another translation unit"));
       return false;
     }
-  if (COMPLETE_TYPE_P (t1) && COMPLETE_TYPE_P (t2)
+  if (DEFINITE_TYPE_P (t1)
+      && DEFINITE_TYPE_P (t2)
       && TYPE_ALIGN (t1) != TYPE_ALIGN (t2))
     {
       warn_odr (t1, t2, NULL, NULL, warn, warned,
@@ -1698,12 +1700,12 @@ add_type_duplicate (odr_type val, tree t
       build_bases = true;
     }
   /* Always prefer complete type to be the leader.  */
-  else if (!COMPLETE_TYPE_P (val->type) && COMPLETE_TYPE_P (type))
+  else if (!DEFINITE_TYPE_P (val->type) && DEFINITE_TYPE_P (type))
     {
       prevail = true;
       build_bases = TYPE_BINFO (type);
     }
-  else if (COMPLETE_TYPE_P (val->type) && !COMPLETE_TYPE_P (type))
+  else if (DEFINITE_TYPE_P (val->type) && !DEFINITE_TYPE_P (type))
     ;
   else if (TREE_CODE (val->type) == ENUMERAL_TYPE
 	   && TREE_CODE (type) == ENUMERAL_TYPE
@@ -1740,7 +1742,8 @@ add_type_duplicate (odr_type val, tree t
   vec_safe_push (val->types, type);
 
   /* If both are class types, compare the bases.  */
-  if (COMPLETE_TYPE_P (type) && COMPLETE_TYPE_P (val->type)
+  if (DEFINITE_TYPE_P (type)
+      && DEFINITE_TYPE_P (val->type)
       && TREE_CODE (val->type) == RECORD_TYPE
       && TREE_CODE (type) == RECORD_TYPE
       && TYPE_BINFO (val->type) && TYPE_BINFO (type))
@@ -1879,7 +1882,8 @@ add_type_duplicate (odr_type val, tree t
   gcc_assert (val->odr_violated || !odr_must_violate);
   /* Sanity check that all bases will be build same way again.  */
   if (flag_checking
-      && COMPLETE_TYPE_P (type) && COMPLETE_TYPE_P (val->type)
+      && DEFINITE_TYPE_P (type)
+      && DEFINITE_TYPE_P (val->type)
       && TREE_CODE (val->type) == RECORD_TYPE
       && TREE_CODE (type) == RECORD_TYPE
       && TYPE_BINFO (val->type) && TYPE_BINFO (type)
@@ -2059,7 +2063,7 @@ get_odr_type (tree type, bool insert)
         val->anonymous_namespace = type_in_anonymous_namespace_p (type);
       else
 	val->anonymous_namespace = 0;
-      build_bases = COMPLETE_TYPE_P (val->type);
+      build_bases = DEFINITE_TYPE_P (val->type);
       insert_to_odr_array = true;
       if (slot)
         *slot = val;
Index: gcc/ipa-icf.c
===================================================================
--- gcc/ipa-icf.c	2018-10-05 13:46:14.391761673 +0100
+++ gcc/ipa-icf.c	2018-10-15 14:12:59.032511711 +0100
@@ -1584,8 +1584,8 @@ sem_item::add_type (const_tree type, inc
     }
   else if (RECORD_OR_UNION_TYPE_P (type))
     {
-      /* Incomplete types must be skipped here.  */
-      if (!COMPLETE_TYPE_P (type))
+      /* Indefinite types must be skipped here.  */
+      if (!DEFINITE_TYPE_P (type))
 	{
 	  hstate.add_int (RECORD_TYPE);
 	  return;
Index: gcc/langhooks.c
===================================================================
--- gcc/langhooks.c	2018-10-05 13:46:11.107788274 +0100
+++ gcc/langhooks.c	2018-10-15 14:12:59.032511711 +0100
@@ -593,8 +593,8 @@ lhd_omp_firstprivatize_type_sizes (struc
 bool
 lhd_omp_mappable_type (tree type)
 {
-  /* Mappable type has to be complete.  */
-  if (type == error_mark_node || !COMPLETE_TYPE_P (type))
+  /* Mappable type has to be definite.  */
+  if (type == error_mark_node || !DEFINITE_TYPE_P (type))
     return false;
   return true;
 }
Index: gcc/omp-low.c
===================================================================
--- gcc/omp-low.c	2018-08-28 11:25:45.614885279 +0100
+++ gcc/omp-low.c	2018-10-15 14:12:59.032511711 +0100
@@ -1023,7 +1023,7 @@ scan_sharing_clauses (tree clauses, omp_
 	      break;
 	    }
 	  gcc_assert (is_taskreg_ctx (ctx));
-	  gcc_assert (!COMPLETE_TYPE_P (TREE_TYPE (decl))
+	  gcc_assert (!DEFINITE_TYPE_P (TREE_TYPE (decl))
 		      || !is_variable_sized (decl));
 	  /* Global variables don't need to be copied,
 	     the receiver side will use them directly.  */
@@ -1429,7 +1429,7 @@ scan_sharing_clauses (tree clauses, omp_
 	      if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
 		   || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER)
 		  && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
-		  && !COMPLETE_TYPE_P (TREE_TYPE (decl)))
+		  && !DEFINITE_TYPE_P (TREE_TYPE (decl)))
 		{
 		  tree new_decl = lookup_decl (decl, ctx);
 		  TREE_TYPE (new_decl)
Index: gcc/tree-ssa-sccvn.c
===================================================================
--- gcc/tree-ssa-sccvn.c	2018-10-05 13:46:08.871806387 +0100
+++ gcc/tree-ssa-sccvn.c	2018-10-15 14:12:59.032511711 +0100
@@ -1424,7 +1424,7 @@ fully_constant_vn_reference_p (vn_refere
 
   /* Simplify reads from constants or constant initializers.  */
   else if (BITS_PER_UNIT == 8
-	   && COMPLETE_TYPE_P (ref->type)
+	   && DEFINITE_TYPE_P (ref->type)
 	   && is_gimple_reg_type (ref->type))
     {
       poly_int64 off = 0;
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	2018-10-05 13:46:08.863806452 +0100
+++ gcc/tree.c	2018-10-15 14:12:59.036511679 +0100
@@ -6523,7 +6523,8 @@ type_cache_hasher::equal (type_hash *a,
   /* Be careful about comparing arrays before and after the element type
      has been completed; don't compare TYPE_ALIGN unless both types are
      complete.  */
-  if (COMPLETE_TYPE_P (a->type) && COMPLETE_TYPE_P (b->type)
+  if (DEFINITE_TYPE_P (a->type)
+      && DEFINITE_TYPE_P (b->type)
       && (TYPE_ALIGN (a->type) != TYPE_ALIGN (b->type)
 	  || TYPE_MODE (a->type) != TYPE_MODE (b->type)))
     return 0;
@@ -8083,7 +8084,7 @@ build_function_type (tree value_type, tr
     TYPE_CANONICAL (t) = build_function_type (TYPE_CANONICAL (value_type),
 					      canon_argtypes);
 
-  if (!COMPLETE_TYPE_P (t))
+  if (!DEFINITE_TYPE_P (t))
     layout_type (t);
   return t;
 }
@@ -8242,7 +8243,7 @@ build_method_type_directly (tree basetyp
       = build_method_type_directly (TYPE_CANONICAL (basetype),
 				    TYPE_CANONICAL (rettype),
 				    canon_argtypes);
-  if (!COMPLETE_TYPE_P (t))
+  if (!DEFINITE_TYPE_P (t))
     layout_type (t);
 
   return t;
@@ -8282,7 +8283,7 @@ build_offset_type (tree basetype, tree t
   hashval_t hash = type_hash_canon_hash (t);
   t = type_hash_canon (hash, t);
 
-  if (!COMPLETE_TYPE_P (t))
+  if (!DEFINITE_TYPE_P (t))
     layout_type (t);
 
   if (TYPE_CANONICAL (t) == t)
@@ -8328,7 +8329,7 @@ build_complex_type (tree component_type,
       /* We created a new type.  The hash insertion will have laid
 	 out the type.  We need to check the canonicalization and
 	 maybe set the name.  */
-      gcc_checking_assert (COMPLETE_TYPE_P (t)
+      gcc_checking_assert (DEFINITE_TYPE_P (t)
 			   && !TYPE_NAME (t)
 			   && TYPE_CANONICAL (t) == t);
 
@@ -13216,12 +13217,12 @@ #define verify_variant_match(flag)
   else
     verify_variant_match (TYPE_SATURATING);
   /* FIXME: This check trigger during libstdc++ build.  */
-  if (RECORD_OR_UNION_TYPE_P (t) && COMPLETE_TYPE_P (t) && 0)
+  if (RECORD_OR_UNION_TYPE_P (t) && DEFINITE_TYPE_P (t) && 0)
     verify_variant_match (TYPE_FINAL_P);
 
   /* tree_type_common checks.  */
 
-  if (COMPLETE_TYPE_P (t))
+  if (DEFINITE_TYPE_P (t))
     {
       verify_variant_match (TYPE_MODE);
       if (TREE_CODE (TYPE_SIZE (t)) != PLACEHOLDER_EXPR
@@ -13274,7 +13275,7 @@ #define verify_variant_match(flag)
       debug_tree (tv);
       return false;
     }
-  if ((TREE_CODE (t) == ENUMERAL_TYPE && COMPLETE_TYPE_P (t))
+  if ((TREE_CODE (t) == ENUMERAL_TYPE && DEFINITE_TYPE_P (t))
        || TREE_CODE (t) == INTEGER_TYPE
        || TREE_CODE (t) == BOOLEAN_TYPE
        || TREE_CODE (t) == REAL_TYPE
@@ -13293,7 +13294,7 @@ #define verify_variant_match(flag)
      or even type's main variant.  This is needed to make bootstrap pass
      and the bug seems new in GCC 5.
      C++ FE should be updated to make this consistent and we should check
-     that TYPE_BINFO is always NULL for !COMPLETE_TYPE_P and otherwise there
+     that TYPE_BINFO is always NULL for !DEFINITE_TYPE_P and otherwise there
      is a match with main variant.
 
      Also disable the check for Java for now because of parser hack that builds
@@ -13323,7 +13324,7 @@ #define verify_variant_match(flag)
   /* Permit incomplete variants of complete type.  While FEs may complete
      all variants, this does not happen for C++ templates in all cases.  */
   else if (RECORD_OR_UNION_TYPE_P (t)
-	   && COMPLETE_TYPE_P (t)
+	   && DEFINITE_TYPE_P (t)
 	   && TYPE_FIELDS (t) != TYPE_FIELDS (tv))
     {
       tree f1, f2;
@@ -13625,7 +13626,7 @@ gimple_canonical_types_compatible_p (con
 
 	/* Don't try to compare variants of an incomplete type, before
 	   TYPE_FIELDS has been copied around.  */
-	if (!COMPLETE_TYPE_P (t1) && !COMPLETE_TYPE_P (t2))
+	if (!DEFINITE_TYPE_P (t1) && !DEFINITE_TYPE_P (t2))
 	  return true;
 
 
@@ -13723,7 +13724,8 @@ verify_type (const_tree t)
       error_found = true;
     }
 
-  if (COMPLETE_TYPE_P (t) && TYPE_CANONICAL (t)
+  if (DEFINITE_TYPE_P (t)
+      && TYPE_CANONICAL (t)
       && TYPE_MODE (t) != TYPE_MODE (TYPE_CANONICAL (t)))
     {
       error ("TYPE_MODE of TYPE_CANONICAL is not compatible");
@@ -13897,7 +13899,7 @@ verify_type (const_tree t)
     }
   else if (RECORD_OR_UNION_TYPE_P (t))
     {
-      if (TYPE_FIELDS (t) && !COMPLETE_TYPE_P (t) && in_lto_p)
+      if (TYPE_FIELDS (t) && !DEFINITE_TYPE_P (t) && in_lto_p)
 	{
 	  error ("TYPE_FIELDS defined in incomplete type");
 	  error_found = true;
Index: gcc/ada/gcc-interface/decl.c
===================================================================
--- gcc/ada/gcc-interface/decl.c	2018-10-15 14:08:45.694611103 +0100
+++ gcc/ada/gcc-interface/decl.c	2018-10-15 14:12:59.020511811 +0100
@@ -2118,7 +2118,7 @@ gnat_to_gnu_entity (Entity_Id gnat_entit
 	   Var are also built later with the fields of the final type, the
 	   aliasing machinery may consider that the accesses are distinct
 	   if the FIELD_DECLs are distinct as objects.  */
-	if (COMPLETE_TYPE_P (gnu_fat_type))
+	if (DEFINITE_TYPE_P (gnu_fat_type))
 	  {
 	    tem = TYPE_FIELDS (gnu_fat_type);
 	    TREE_TYPE (tem) = ptr_type_node;
Index: gcc/ada/gcc-interface/utils2.c
===================================================================
--- gcc/ada/gcc-interface/utils2.c	2018-05-02 08:38:09.069414813 +0100
+++ gcc/ada/gcc-interface/utils2.c	2018-10-15 14:12:59.020511811 +0100
@@ -2004,7 +2004,7 @@ build_simple_component_ref (tree record,
   tree type = TYPE_MAIN_VARIANT (TREE_TYPE (record));
   tree ref;
 
-  gcc_assert (RECORD_OR_UNION_TYPE_P (type) && COMPLETE_TYPE_P (type));
+  gcc_assert (RECORD_OR_UNION_TYPE_P (type) && DEFINITE_TYPE_P (type));
 
   /* Try to fold a conversion from another record or union type unless the type
      contains a placeholder as it might be needed for a later substitution.  */
Index: gcc/fortran/trans-decl.c
===================================================================
--- gcc/fortran/trans-decl.c	2018-10-05 13:46:10.559792714 +0100
+++ gcc/fortran/trans-decl.c	2018-10-15 14:12:59.028511745 +0100
@@ -1059,7 +1059,7 @@ gfc_build_qualified_array (tree decl, gf
       type = TREE_TYPE (type);
     }
 
-  if (! COMPLETE_TYPE_P (type) && GFC_TYPE_ARRAY_SIZE (type))
+  if (!DEFINITE_TYPE_P (type) && GFC_TYPE_ARRAY_SIZE (type))
     {
       tree size, range;
 
@@ -2542,7 +2542,7 @@ create_function_arglist (gfc_symbol * sy
 	  && TREE_CODE (type) == POINTER_TYPE
 	  && GFC_ARRAY_TYPE_P (type)
 	  && f->sym->as->type != AS_ASSUMED_SIZE
-	  && ! COMPLETE_TYPE_P (TREE_TYPE (type)))
+	  && !DEFINITE_TYPE_P (TREE_TYPE (type)))
 	{
 	  if (f->sym->attr.flavor == FL_PROCEDURE)
 	    type = build_pointer_type (gfc_get_function_type (f->sym));
Index: gcc/lto/lto-symtab.c
===================================================================
--- gcc/lto/lto-symtab.c	2018-10-15 14:12:54.040553089 +0100
+++ gcc/lto/lto-symtab.c	2018-10-15 14:12:59.032511711 +0100
@@ -787,8 +787,8 @@ lto_symtab_merge_decls_1 (symtab_node *f
 	{
 	  for (e = prevailing->next_sharing_asm_name;
 	       e; e = e->next_sharing_asm_name)
-	    if (!COMPLETE_TYPE_P (TREE_TYPE (prevailing->decl))
-		&& COMPLETE_TYPE_P (TREE_TYPE (e->decl))
+	    if (!DEFINITE_TYPE_P (TREE_TYPE (prevailing->decl))
+		&& DEFINITE_TYPE_P (TREE_TYPE (e->decl))
 		&& lto_symtab_symbol_p (e))
 	      prevailing = e;
 	}

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [04/10] Move COMPLETE_OR_UNBOUND_ARRAY_TYPE_P to the C and C++ frontends
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (2 preceding siblings ...)
  2018-10-15 14:34 ` [02/10] Replace most uses of COMPLETE_TYPE_P outside the frontends Richard Sandiford
@ 2018-10-15 14:35 ` Richard Sandiford
  2018-10-15 14:36 ` [05/10] Move complete_or_array_type_p " Richard Sandiford
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

There was only one use of COMPLETE_OR_UNBOUND_ARRAY_TYPE_P outside the
frontends, in expr.c.  This patch expands the macro there and moves the
macro's definition to c-common.h.

It feels a bit odd that we still have decls with no layout at
this late stage, but that's a separate issue...

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree.h (COMPLETE_OR_UNBOUND_ARRAY_TYPE_P): Move to c-common.h.
	* expr.c (expand_expr_real_1): Expand use of
	COMPLETE_OR_UNBOUND_ARRAY_TYPE_P here.

gcc/c-family/
	* c-common.h (COMPLETE_OR_UNBOUND_ARRAY_TYPE_P): New macro,
	moved from tree.h.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-15 14:13:04.148469305 +0100
+++ gcc/tree.h	2018-10-15 14:13:08.520433065 +0100
@@ -602,10 +602,6 @@ #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE
 /* Nonzero if this type is the (possibly qualified) void type.  */
 #define VOID_TYPE_P(NODE) (TREE_CODE (NODE) == VOID_TYPE)
 
-/* Nonzero if this type is complete or is an array with unspecified bound.  */
-#define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
-  (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
-
 #define FUNC_OR_METHOD_TYPE_P(NODE) \
   (TREE_CODE (NODE) == FUNCTION_TYPE || TREE_CODE (NODE) == METHOD_TYPE)
 
Index: gcc/expr.c
===================================================================
--- gcc/expr.c	2018-10-15 14:12:54.040553089 +0100
+++ gcc/expr.c	2018-10-15 14:13:08.520433065 +0100
@@ -9884,7 +9884,8 @@ expand_expr_real_1 (tree exp, rtx target
       /* If a static var's type was incomplete when the decl was written,
 	 but the type is complete now, lay out the decl now.  */
       if (DECL_SIZE (exp) == 0
-	  && COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (TREE_TYPE (exp))
+	  && DEFINITE_TYPE_P (TREE_CODE (type) == ARRAY_TYPE
+			      ? TREE_TYPE (type) : type)
 	  && (TREE_STATIC (exp) || DECL_EXTERNAL (exp)))
 	layout_decl (exp, 0);
 
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:04.148469305 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:08.516433099 +0100
@@ -746,6 +746,10 @@ #define C_TYPE_OBJECT_OR_INCOMPLETE_P(ty
 #define COMPLETE_OR_VOID_TYPE_P(NODE) \
   (COMPLETE_TYPE_P (NODE) || VOID_TYPE_P (NODE))
 
+/* Nonzero if this type is complete or is an array with unspecified bound.  */
+#define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
+  (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
+
 struct visibility_flags
 {
   unsigned inpragma : 1;	/* True when in #pragma GCC visibility.  */

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [05/10] Move complete_or_array_type_p to the C and C++ frontends
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (3 preceding siblings ...)
  2018-10-15 14:35 ` [04/10] Move COMPLETE_OR_UNBOUND_ARRAY_TYPE_P to the C and C++ frontends Richard Sandiford
@ 2018-10-15 14:36 ` Richard Sandiford
  2018-10-15 14:36 ` [06/10] Move COMPLETE_TYPE_P " Richard Sandiford
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

complete_or_array_type_p was defined in tree.h but unused outside
the frontends.  This patch moves it to c-common.h.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree.h (complete_or_array_type_p): Move to c-common.h.

gcc/c-family/
	* c-common.h (complete_or_array_type_p): Moved from tree.h.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-15 14:13:08.520433065 +0100
+++ gcc/tree.h	2018-10-15 14:13:13.584391090 +0100
@@ -4859,17 +4859,6 @@ ptrofftype_p (tree type)
 	  && TYPE_UNSIGNED (type) == TYPE_UNSIGNED (sizetype));
 }
 
-/* Return true if the argument is a complete type or an array
-   of unknown bound (whose type is incomplete but) whose elements
-   have complete type.  */
-static inline bool
-complete_or_array_type_p (const_tree type)
-{
-  return COMPLETE_TYPE_P (type)
-         || (TREE_CODE (type) == ARRAY_TYPE
-	     && COMPLETE_TYPE_P (TREE_TYPE (type)));
-}
-
 /* Return true if the value of T could be represented as a poly_widest_int.  */
 
 inline bool
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:08.516433099 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:13.584391090 +0100
@@ -750,6 +750,17 @@ #define COMPLETE_OR_VOID_TYPE_P(NODE) \
 #define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
   (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
 
+/* Return true if the argument is a complete type or an array
+   of unknown bound (whose type is incomplete but) whose elements
+   have complete type.  */
+static inline bool
+complete_or_array_type_p (const_tree type)
+{
+  return COMPLETE_TYPE_P (type)
+         || (TREE_CODE (type) == ARRAY_TYPE
+	     && COMPLETE_TYPE_P (TREE_TYPE (type)));
+}
+
 struct visibility_flags
 {
   unsigned inpragma : 1;	/* True when in #pragma GCC visibility.  */

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [06/10] Move COMPLETE_TYPE_P to the C and C++ frontends
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (4 preceding siblings ...)
  2018-10-15 14:36 ` [05/10] Move complete_or_array_type_p " Richard Sandiford
@ 2018-10-15 14:36 ` Richard Sandiford
  2018-10-15 14:37 ` [07/10] Use COMPLETE_TYPE_P instead of TYPE_SIZE Richard Sandiford
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

After previous patches there are no more uses of COMPLETE_TYPE_P outside
the frontends.  This patch moves the definition to c-common.h.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree.h (COMPLETE_TYPE_P): Move to c-common.h.

gcc/c-family/
	* c-common.h (COMPLETE_TYPE_P): Moved from tree.h.
	* c-ada-spec.c: Include c-common.h.

Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-15 14:13:13.584391090 +0100
+++ gcc/tree.h	2018-10-15 14:13:18.280352163 +0100
@@ -596,9 +596,6 @@ #define FUNCTION_POINTER_TYPE_P(TYPE) \
    to create objects of that type.  The type might be sized or sizeless.  */
 #define DEFINITE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
 
-/* Nonzero if this type is a complete type.  */
-#define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
-
 /* Nonzero if this type is the (possibly qualified) void type.  */
 #define VOID_TYPE_P(NODE) (TREE_CODE (NODE) == VOID_TYPE)
 
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:13.584391090 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:18.280352163 +0100
@@ -726,6 +726,9 @@ enum cxx_dialect {
 
 extern bool done_lexing;
 
+/* Nonzero if this type is a complete type.  */
+#define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
+
 /* C types are partitioned into three subsets: object, function, and
    incomplete types.  */
 #define C_TYPE_OBJECT_P(type) \
Index: gcc/c-family/c-ada-spec.c
===================================================================
--- gcc/c-family/c-ada-spec.c	2018-10-05 13:46:08.287811117 +0100
+++ gcc/c-family/c-ada-spec.c	2018-10-15 14:13:18.280352163 +0100
@@ -27,6 +27,7 @@ Software Foundation; either version 3, o
 #include "c-ada-spec.h"
 #include "fold-const.h"
 #include "c-pragma.h"
+#include "c-common.h"
 #include "diagnostic.h"
 #include "stringpool.h"
 #include "attribs.h"

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [07/10] Use COMPLETE_TYPE_P instead of TYPE_SIZE
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (5 preceding siblings ...)
  2018-10-15 14:36 ` [06/10] Move COMPLETE_TYPE_P " Richard Sandiford
@ 2018-10-15 14:37 ` Richard Sandiford
  2018-10-15 14:38 ` [08/10] Add a TYPE_SIZELESS_P property to types Richard Sandiford
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:37 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

This patch makes a couple of c-family macros use COMPLETE_TYPE_P instead
of TYPE_SIZE, so that the definitions more clearly correspond to the
names of the macros.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (C_TYPE_OBJECT_P, C_TYPE_INCOMPLETE_P): Test
	COMPLETE_TYPE_P instead of TYPE_SIZE.

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:18.280352163 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:22.880314033 +0100
@@ -732,10 +732,10 @@ #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE
 /* C types are partitioned into three subsets: object, function, and
    incomplete types.  */
 #define C_TYPE_OBJECT_P(type) \
-  (TREE_CODE (type) != FUNCTION_TYPE && TYPE_SIZE (type))
+  (TREE_CODE (type) != FUNCTION_TYPE && COMPLETE_TYPE_P (type))
 
 #define C_TYPE_INCOMPLETE_P(type) \
-  (TREE_CODE (type) != FUNCTION_TYPE && TYPE_SIZE (type) == 0)
+  (TREE_CODE (type) != FUNCTION_TYPE && !COMPLETE_TYPE_P (type))
 
 #define C_TYPE_FUNCTION_P(type) \
   (TREE_CODE (type) == FUNCTION_TYPE)

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [08/10] Add a TYPE_SIZELESS_P property to types
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (6 preceding siblings ...)
  2018-10-15 14:37 ` [07/10] Use COMPLETE_TYPE_P instead of TYPE_SIZE Richard Sandiford
@ 2018-10-15 14:38 ` Richard Sandiford
  2018-10-15 14:50 ` [09/10] C support for sizeless types Richard Sandiford
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

This patch adds a bit to tree_type_common (which still has plenty
of bits spare) to indicate whether the type has a measurable size
at the language level once fully-defined.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/
	* tree-core.h (tree_type_common::sizeless): New bitfield.
	(tree_type_common::spare): Reduce size by one bit.
	* tree.h (TYPE_SIZELESS_P): New macro.

gcc/c-family/
	* c-common.h (COMPLETE_TYPE_P): Check TYPE_SIZELESS_P.

Index: gcc/tree-core.h
===================================================================
--- gcc/tree-core.h	2018-10-05 13:46:11.195787561 +0100
+++ gcc/tree-core.h	2018-10-15 14:13:28.592266684 +0100
@@ -1534,7 +1534,8 @@ struct GTY(()) tree_type_common {
   unsigned warn_if_not_align : 6;
   unsigned typeless_storage : 1;
   unsigned empty_flag : 1;
-  unsigned spare : 17;
+  unsigned sizeless : 1;
+  unsigned spare : 16;
 
   alias_set_type alias_set;
   tree pointer_to;
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	2018-10-15 14:13:18.280352163 +0100
+++ gcc/tree.h	2018-10-15 14:13:28.596266651 +0100
@@ -688,6 +688,13 @@ #define TRANSLATION_UNIT_WARN_EMPTY_P(NO
 /* Nonzero if this type is "empty" according to the particular psABI.  */
 #define TYPE_EMPTY_P(NODE) (TYPE_CHECK (NODE)->type_common.empty_flag)
 
+/* True if this type is "sizeless" according to the SVE extensions to
+   C and C++.  Sizeless types have no measurable size or alignment at
+   the language level, even when the types have been fully defined
+   (are "definite").  Size, alignment and layout are instead decided
+   by the ABI.  */
+#define TYPE_SIZELESS_P(NODE) (TYPE_CHECK (NODE)->type_common.sizeless)
+
 /* Used to indicate that this TYPE represents a compiler-generated entity.  */
 #define TYPE_ARTIFICIAL(NODE) (TYPE_CHECK (NODE)->base.nowarning_flag)
 
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:22.880314033 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:28.592266684 +0100
@@ -727,7 +727,8 @@ enum cxx_dialect {
 extern bool done_lexing;
 
 /* Nonzero if this type is a complete type.  */
-#define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
+#define COMPLETE_TYPE_P(NODE) \
+  (TYPE_SIZE (NODE) != NULL_TREE && !TYPE_SIZELESS_P (NODE))
 
 /* C types are partitioned into three subsets: object, function, and
    incomplete types.  */

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [09/10] C support for sizeless types
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (7 preceding siblings ...)
  2018-10-15 14:38 ` [08/10] Add a TYPE_SIZELESS_P property to types Richard Sandiford
@ 2018-10-15 14:50 ` Richard Sandiford
  2018-10-15 15:01 ` [10/10] C++ " Richard Sandiford
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 14:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

[-- Attachment #1: Type: text/plain, Size: 43616 bytes --]

This patch adds support for sizeless types to C, along the lines
described in the covering RFC.  The patch is actually a squash
of 26 patches that I've attached as a tarball, with each patch
building up the support piece-by-piece.  The individual patches
say which part of the standard they relate to and add associated
tests to gcc.dg/sizeless-1.c.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (RID_SIZELESS_STRUCT): New rid enum.
	(DEFINITE_OR_VOID_TYPE_P): New macro.
	(DEFINITE_OR_UNBOUND_ARRAY_TYPE_P): Likewise.
	* c-common.c (c_common_reswords): Add __sizeless_struct.
	(complete_size_in_bytes): New function.
	(pointer_int_sum): Use it instead of size_in_bytes_loc.
	(c_alignof_expr): Pass sizeless types directly to c_alignof.

gcc/c/
	* c-tree.h (C_TYPE_INCOMPLETE_VARS): Rename to...
	(C_TYPE_INDEFINITE_VARS): ...this.
	(start_struct, parser_xref_tag): Add a bool parameter.
	(require_complete_type): Add a default false parameter.
	(require_definite_type): New function.
	* c-decl.c (pushdecl): Use DEFINITE_TYPE_P instead of
	COMPLETE_TYPE_P.  Update after above name change.
	(lookup_tag): Add a sizeless_p parameter.  Check that it
	matches the TYPE_SIZELESS_P field of any existing type with
	the same name.
	(shadow_tag_warned): Update call accordingly.
	(start_decl): Require variables to have definite rather than
	complete type.  Don't reject initializers for variable-sized
	objects with sizeless type.
	(finish_decl): Reject sizeless objects with static or
	thread-local storage duration.
	(build_compound_literal): Require compound literals to have
	definite rather than complete type.
	(grokdeclarator): Likewise fields.
	(grokparms): Likewise function parameters.
	(parser_xref_tag): Add a sizeless_p parameter.  Update call to
	lookup_tag.  Initialize TYPE_SIZELESS_P when creating a new type.
	(start_struct): Likewise.
	(xref_tag): Update call to parser_xref_tag.
	(finish_struct): Reject flexible array members in sizeless
	structures.  Reject sizeless fields in sized aggregates.
	Update after above name change.
	(start_enum): Update call to lookup_tag.
	(start_function): Require the return type to be definite rather
	than complete.
	(store_parm_decls_oldstyle): Likewise function parameters.
	* c-parser.c (c_parser_declspecs): Handle RID_SIZELESS_STRUCT.
	(c_parser_struct_or_union_specifier): Likewise.  Update calls
	to start_struct and parser_xref_tag.
	(c_parser_enum_specifier): Update call to parser_xref_tag.
	(c_parser_generic_selection): Require the type in a _Generic
	association to be definite rather than complete.
	(c_parser_objc_selector): Handle RID_SIZELESS_STRUCT (but
	commented out).
	(c_parser_omp_threadprivate): Add a comment.
	* c-typeck.c (require_complete_type): Add an allow_sizeless_p
	parameter.
	(default_conversion): Require the type to be definite rather than
	complete when processing the expression being converted.
	(build_component_ref): Likewise when processing member accesses.
	(build_indirect_ref): Likewise when processing pointer dereferences.
	(build_function_call_vec): Likewise when processing the return
	type of a function call.
	(convert_arguments): Likewise when processing the types of the
	formal and actual parameters.
	(build_unary_op): Likewise when processing the operand of a
	unary operation.
	(build_c_cast): Likewise when processing the value being cast.
	(build_modify_expr): Likewise when processing the lhs of an
	assignment.
	(convert_for_assignment): Likewise when processing the rhs
	of an assignment.
	(c_process_expr_stmt): Likewise when processing the type of
	a statement expression.
	(c_build_va_arg): Likewise when processing the second argument
	of a va_arg call.
	(c_build_qualified_type): Update after above name change.

gcc/objc/
	* objc-runtime-shared-support.c (objc_start_struct): Update call
	to start_struct.

gcc/objcp/
	* objcp-decl.h (start_struct): Add a sizeless_p parameter.

gcc/testsuite/
	* gcc.dg/sizeless-1.c: New test.

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:28.592266684 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:32.988230244 +0100
@@ -104,6 +104,7 @@ enum rid
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_TGMATH,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
+  RID_SIZELESS_STRUCT,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
   RID_FLOAT16,
@@ -750,10 +751,18 @@ #define C_TYPE_OBJECT_OR_INCOMPLETE_P(ty
 #define COMPLETE_OR_VOID_TYPE_P(NODE) \
   (COMPLETE_TYPE_P (NODE) || VOID_TYPE_P (NODE))
 
+/* Nonzero if this type is definite or is cv void.  */
+#define DEFINITE_OR_VOID_TYPE_P(NODE) \
+  (DEFINITE_TYPE_P (NODE) || VOID_TYPE_P (NODE))
+
 /* Nonzero if this type is complete or is an array with unspecified bound.  */
 #define COMPLETE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
   (COMPLETE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
 
+/* Nonzero if this type is definite or is an array with unspecified bound.  */
+#define DEFINITE_OR_UNBOUND_ARRAY_TYPE_P(NODE) \
+  (DEFINITE_TYPE_P (TREE_CODE (NODE) == ARRAY_TYPE ? TREE_TYPE (NODE) : (NODE)))
+
 /* Return true if the argument is a complete type or an array
    of unknown bound (whose type is incomplete but) whose elements
    have complete type.  */
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	2018-10-15 14:08:44.946617301 +0100
+++ gcc/c-family/c-common.c	2018-10-15 14:13:32.988230244 +0100
@@ -425,6 +425,7 @@ const struct c_common_resword c_common_r
   { "__restrict__",	RID_RESTRICT,	0 },
   { "__signed",		RID_SIGNED,	0 },
   { "__signed__",	RID_SIGNED,	0 },
+  { "__sizeless_struct", RID_SIZELESS_STRUCT, 0 },
   { "__thread",		RID_THREAD,	0 },
   { "__transaction_atomic", RID_TRANSACTION_ATOMIC, 0 },
   { "__transaction_relaxed", RID_TRANSACTION_RELAXED, 0 },
@@ -3067,6 +3068,32 @@ shorten_compare (location_t loc, tree *o
   return NULL_TREE;
 }
 \f
+/* Return the size nominally occupied by an object of type TYPE
+   when it resides in memory.  The value is measured in units of bytes,
+   and its data type is that normally used for type sizes
+   (which is the first type created by make_signed_type or
+   make_unsigned_type).
+
+   Return error_mark_node if TYPE is incomplete, and in addition
+   raise an error if COMPLAIN is true.  LOC is the location to use
+   for reporting error messages.  */
+
+tree
+complete_size_in_bytes (location_t loc, const_tree type, bool complain)
+{
+  if (type == error_mark_node)
+    return error_mark_node;
+
+  if (!COMPLETE_TYPE_P (type))
+    {
+      if (complain)
+	lang_hooks.types.incomplete_type_error (loc, NULL_TREE, type);
+      return error_mark_node;
+    }
+
+  return TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type));
+}
+
 /* Return a tree for the sum or difference (RESULTCODE says which)
    of pointer PTROP and integer INTOP.  */
 
@@ -3098,7 +3125,16 @@ pointer_int_sum (location_t loc, enum tr
       size_exp = integer_one_node;
     }
   else
-    size_exp = size_in_bytes_loc (loc, TREE_TYPE (result_type));
+    {
+      size_exp = complete_size_in_bytes (loc, TREE_TYPE (result_type),
+					 complain);
+      if (size_exp == error_mark_node)
+	{
+	  if (!complain)
+	    return size_exp;
+	  size_exp = integer_one_node;
+	}
+    }
 
   /* We are manipulating pointer values, so we don't need to warn
      about relying on undefined signed overflow.  We disable the
@@ -3663,6 +3699,10 @@ c_alignof_expr (location_t loc, tree exp
 {
   tree t;
 
+  if (TREE_TYPE (expr) != error_mark_node
+      && TYPE_SIZELESS_P (TREE_TYPE (expr)))
+    return c_alignof (loc, TREE_TYPE (expr));
+
   if (VAR_OR_FUNCTION_DECL_P (expr))
     t = size_int (DECL_ALIGN_UNIT (expr));
 
Index: gcc/c/c-tree.h
===================================================================
--- gcc/c/c-tree.h	2018-08-28 11:25:45.530886001 +0100
+++ gcc/c/c-tree.h	2018-10-15 14:13:32.996230178 +0100
@@ -38,9 +38,9 @@ #define C_TYPE_FIELDS_VOLATILE(TYPE) TRE
    nonzero if the definition of the type has already started.  */
 #define C_TYPE_BEING_DEFINED(TYPE) TYPE_LANG_FLAG_0 (TYPE)
 
-/* In an incomplete RECORD_TYPE or UNION_TYPE, a list of variable
+/* In an indefinite RECORD_TYPE or UNION_TYPE, a list of variable
    declarations whose type would be completed by completing that type.  */
-#define C_TYPE_INCOMPLETE_VARS(TYPE) TYPE_VFIELD (TYPE)
+#define C_TYPE_INDEFINITE_VARS(TYPE) TYPE_VFIELD (TYPE)
 
 /* In an IDENTIFIER_NODE, nonzero if this identifier is actually a
    keyword.  C_RID_CODE (node) is then the RID_* value of the keyword.  */
@@ -574,14 +574,15 @@ extern tree start_enum (location_t, stru
 extern bool start_function (struct c_declspecs *, struct c_declarator *, tree);
 extern tree start_decl (struct c_declarator *, struct c_declspecs *, bool,
 			tree);
-extern tree start_struct (location_t, enum tree_code, tree,
+extern tree start_struct (location_t, enum tree_code, bool, tree,
 			  struct c_struct_parse_info **);
 extern void store_parm_decls (void);
 extern void store_parm_decls_from (struct c_arg_info *);
 extern void temp_store_parm_decls (tree, tree);
 extern void temp_pop_parm_decls (void);
 extern tree xref_tag (enum tree_code, tree);
-extern struct c_typespec parser_xref_tag (location_t, enum tree_code, tree);
+extern struct c_typespec parser_xref_tag (location_t, enum tree_code,
+					  bool, tree);
 extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
 				    struct c_declarator *, location_t);
 extern struct c_declarator *build_attrs_declarator (tree,
@@ -626,7 +627,7 @@ extern bool c_vla_unspec_p (tree x, tree
 extern struct c_switch *c_switch_stack;
 
 extern tree c_objc_common_truthvalue_conversion (location_t, tree);
-extern tree require_complete_type (location_t, tree);
+extern tree require_complete_type (location_t, tree, bool = false);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
 extern int comptypes_check_different_types (tree, tree, bool *);
@@ -774,6 +775,19 @@ set_c_expr_source_range (c_expr *expr,
 /* In c-fold.c */
 extern vec<tree> incomplete_record_decls;
 
+/* Do `exp = require_definite_type (loc, exp);' to make sure exp
+   does not have an indefinite type; i.e. to make sure that the
+   definition of the type has been seen and completely processed.
+   Void types are always indefinite (and thus incomplete).
+
+   LOC is the location of the use.  */
+
+inline tree
+require_definite_type (location_t loc, tree value)
+{
+  return require_complete_type (loc, value, true);
+}
+
 #if CHECKING_P
 namespace selftest {
   extern void run_c_tests (void);
Index: gcc/c/c-decl.c
===================================================================
--- gcc/c/c-decl.c	2018-10-05 13:46:08.319810858 +0100
+++ gcc/c/c-decl.c	2018-10-15 14:13:32.992230210 +0100
@@ -3113,7 +3113,7 @@ pushdecl (tree x)
      slot (e.g. "f(void a, ...)") - that doesn't count as an
      incomplete type.  */
   if (TREE_TYPE (x) != error_mark_node
-      && !COMPLETE_TYPE_P (TREE_TYPE (x)))
+      && !DEFINITE_TYPE_P (TREE_TYPE (x)))
     {
       tree element = TREE_TYPE (x);
 
@@ -3124,9 +3124,9 @@ pushdecl (tree x)
       if (RECORD_OR_UNION_TYPE_P (element)
 	  && (TREE_CODE (x) != TYPE_DECL
 	      || TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE)
-	  && !COMPLETE_TYPE_P (element))
-	C_TYPE_INCOMPLETE_VARS (element)
-	  = tree_cons (NULL_TREE, x, C_TYPE_INCOMPLETE_VARS (element));
+	  && !DEFINITE_TYPE_P (element))
+	C_TYPE_INDEFINITE_VARS (element)
+	  = tree_cons (NULL_TREE, x, C_TYPE_INDEFINITE_VARS (element));
     }
   return x;
 }
@@ -3928,13 +3928,14 @@ c_check_switch_jump_warnings (struct c_s
    If THISLEVEL_ONLY is nonzero, searches only the current_scope.
    CODE says which kind of type the caller wants;
    it is RECORD_TYPE or UNION_TYPE or ENUMERAL_TYPE.
+   SIZELESS_P is true if the type should be sizeless rather than sized.
    If PLOC is not NULL and this returns non-null, it sets *PLOC to the
    location where the tag was defined.
    If the wrong kind of type is found, an error is reported.  */
 
 static tree
-lookup_tag (enum tree_code code, tree name, bool thislevel_only,
-	    location_t *ploc)
+lookup_tag (enum tree_code code, bool sizeless_p, tree name,
+	    bool thislevel_only, location_t *ploc)
 {
   struct c_binding *b = I_TAG_BINDING (name);
   bool thislevel = false;
@@ -3944,7 +3945,9 @@ lookup_tag (enum tree_code code, tree na
 
   /* We only care about whether it's in this level if
      thislevel_only was set or it might be a type clash.  */
-  if (thislevel_only || TREE_CODE (b->decl) != code)
+  bool clash_p = (TREE_CODE (b->decl) != code
+		  || TYPE_SIZELESS_P (b->decl) != sizeless_p);
+  if (thislevel_only || clash_p)
     {
       /* For our purposes, a tag in the external scope is the same as
 	 a tag in the file scope.  (Primarily relevant to Objective-C
@@ -3958,7 +3961,7 @@ lookup_tag (enum tree_code code, tree na
   if (thislevel_only && !thislevel)
     return NULL_TREE;
 
-  if (TREE_CODE (b->decl) != code)
+  if (clash_p)
     {
       /* Definition isn't the kind we were looking for.  */
       pending_invalid_xref = name;
@@ -4346,6 +4349,7 @@ shadow_tag_warned (const struct c_declsp
     {
       tree value = declspecs->type;
       enum tree_code code = TREE_CODE (value);
+      bool sizeless_p = TYPE_SIZELESS_P (value);
 
       if (code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
 	/* Used to test also that TYPE_SIZE (value) != 0.
@@ -4412,7 +4416,7 @@ shadow_tag_warned (const struct c_declsp
 	  else
 	    {
 	      pending_invalid_xref = NULL_TREE;
-	      t = lookup_tag (code, name, true, NULL);
+	      t = lookup_tag (code, sizeless_p, name, true, NULL);
 
 	      if (t == NULL_TREE)
 		{
@@ -4759,12 +4763,13 @@ start_decl (struct c_declarator *declara
 	   We already gave a warning, so we don't need another one.  */
 	if (TREE_TYPE (decl) == error_mark_node)
 	  initialized = false;
-	else if (COMPLETE_TYPE_P (TREE_TYPE (decl)))
+	else if (DEFINITE_TYPE_P (TREE_TYPE (decl)))
 	  {
-	    /* A complete type is ok if size is fixed.  */
+	    /* A definite type is ok if size is fixed.  */
 
-	    if (TREE_CODE (TYPE_SIZE (TREE_TYPE (decl))) != INTEGER_CST
-		|| C_DECL_VARIABLE_SIZE (decl))
+	    if (!TYPE_SIZELESS_P (TREE_TYPE (decl))
+		&& (TREE_CODE (TYPE_SIZE (TREE_TYPE (decl))) != INTEGER_CST
+		    || C_DECL_VARIABLE_SIZE (decl)))
 	      {
 		error ("variable-sized object may not be initialized");
 		initialized = false;
@@ -5046,6 +5051,19 @@ finish_decl (tree decl, location_t init_
 	  && COMPLETE_TYPE_P (TREE_TYPE (decl)))
 	layout_decl (decl, 0);
 
+      if (TREE_TYPE (decl) != error_mark_node
+	  && TYPE_SIZELESS_P (type)
+	  && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+	{
+	  if (DECL_THREAD_LOCAL_P (decl))
+	    error ("sizeless variable %q+D cannot have thread-local"
+		   " storage duration", decl);
+	  else
+	    error ("sizeless variable %q+D cannot have static storage"
+		   " duration", decl);
+	  TREE_TYPE (decl) = error_mark_node;
+	}
+
       if (DECL_SIZE (decl) == NULL_TREE
 	  /* Don't give an error if we already gave one earlier.  */
 	  && TREE_TYPE (decl) != error_mark_node
@@ -5384,7 +5402,7 @@ build_compound_literal (location_t loc,
       TREE_TYPE (DECL_INITIAL (decl)) = type;
     }
 
-  if (type == error_mark_node || !COMPLETE_TYPE_P (type))
+  if (type == error_mark_node || !DEFINITE_TYPE_P (type))
     {
       c_incomplete_type_error (loc, NULL_TREE, type);
       return error_mark_node;
@@ -6913,7 +6931,7 @@ grokdeclarator (const struct c_declarato
 	    type = build_pointer_type (type);
 	  }
 	else if (TREE_CODE (type) != ERROR_MARK
-		 && !COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (type))
+		 && !DEFINITE_OR_UNBOUND_ARRAY_TYPE_P (type))
 	  {
 	    if (name)
 	      error_at (loc, "field %qE has incomplete type", name);
@@ -7252,7 +7270,7 @@ grokparms (struct c_arg_info *arg_info,
 	  if (type == error_mark_node)
 	    continue;
 
-	  if (!COMPLETE_TYPE_P (type))
+	  if (!DEFINITE_TYPE_P (type))
 	    {
 	      if (funcdef_flag)
 		{
@@ -7499,10 +7517,13 @@ get_parm_info (bool ellipsis, tree expr)
 /* Get the struct, enum or union (CODE says which) with tag NAME.
    Define the tag as a forward-reference with location LOC if it is
    not defined.  Return a c_typespec structure for the type
-   specifier.  */
+   specifier.
+
+   SIZELESS_P says whether the type described by CODE is sizeless.  */
 
 struct c_typespec
-parser_xref_tag (location_t loc, enum tree_code code, tree name)
+parser_xref_tag (location_t loc, enum tree_code code, bool sizeless_p,
+		 tree name)
 {
   struct c_typespec ret;
   tree ref;
@@ -7514,7 +7535,7 @@ parser_xref_tag (location_t loc, enum tr
   /* If a cross reference is requested, look up the type
      already defined for this tag and return it.  */
 
-  ref = lookup_tag (code, name, false, &refloc);
+  ref = lookup_tag (code, sizeless_p, name, false, &refloc);
   /* If this is the right type of tag, return what we found.
      (This reference will be shadowed by shadow_tag later if appropriate.)
      If this is the wrong type of tag, do not return it.  If it was the
@@ -7568,6 +7589,7 @@ parser_xref_tag (location_t loc, enum tr
      the forward-reference will be altered into a real type.  */
 
   ref = make_node (code);
+  TYPE_SIZELESS_P (ref) = sizeless_p;
   if (code == ENUMERAL_TYPE)
     {
       /* Give the type a default layout like unsigned int
@@ -7594,13 +7616,15 @@ parser_xref_tag (location_t loc, enum tr
 tree
 xref_tag (enum tree_code code, tree name)
 {
-  return parser_xref_tag (input_location, code, name).spec;
+  /* At present, this function only needs to support sized types.  */
+  return parser_xref_tag (input_location, code, false, name).spec;
 }
 \f
 /* Make sure that the tag NAME is defined *in the current scope*
    at least as a forward reference.
    LOC is the location of the struct's definition.
    CODE says which kind of tag NAME ought to be.
+   SIZELESS_P says whether the associated type should be sizeless.
 
    This stores the current value of the file static STRUCT_PARSE_INFO
    in *ENCLOSING_STRUCT_PARSE_INFO, and points STRUCT_PARSE_INFO at a
@@ -7608,7 +7632,7 @@ xref_tag (enum tree_code code, tree name
    STRUCT_PARSE_INFO is restored in finish_struct.  */
 
 tree
-start_struct (location_t loc, enum tree_code code, tree name,
+start_struct (location_t loc, enum tree_code code, bool sizeless_p, tree name,
 	      struct c_struct_parse_info **enclosing_struct_parse_info)
 {
   /* If there is already a tag defined at this scope
@@ -7618,7 +7642,7 @@ start_struct (location_t loc, enum tree_
   location_t refloc = UNKNOWN_LOCATION;
 
   if (name != NULL_TREE)
-    ref = lookup_tag (code, name, true, &refloc);
+    ref = lookup_tag (code, sizeless_p, name, true, &refloc);
   if (ref && TREE_CODE (ref) == code)
     {
       if (TYPE_STUB_DECL (ref))
@@ -7654,6 +7678,7 @@ start_struct (location_t loc, enum tree_
   if (ref == NULL_TREE || TREE_CODE (ref) != code)
     {
       ref = make_node (code);
+      TYPE_SIZELESS_P (ref) = sizeless_p;
       pushtag (loc, name, ref);
     }
 
@@ -8118,7 +8143,13 @@ finish_struct (location_t loc, tree t, t
 	  && TYPE_DOMAIN (TREE_TYPE (x)) != NULL_TREE
 	  && TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (x))) == NULL_TREE)
 	{
-	  if (TREE_CODE (t) == UNION_TYPE)
+	  if (TYPE_SIZELESS_P (t))
+	    {
+	      error_at (DECL_SOURCE_LOCATION (x),
+			"flexible array member in sizeless struct");
+	      TREE_TYPE (x) = error_mark_node;
+	    }
+	  else if (TREE_CODE (t) == UNION_TYPE)
 	    {
 	      error_at (DECL_SOURCE_LOCATION (x),
 			"flexible array member in union");
@@ -8144,6 +8175,19 @@ finish_struct (location_t loc, tree t, t
 	pedwarn (DECL_SOURCE_LOCATION (x), OPT_Wpedantic,
 		 "invalid use of structure with flexible array member");
 
+      if (TREE_TYPE (x) != error_mark_node
+	  && TYPE_SIZELESS_P (TREE_TYPE (x))
+	  && !TYPE_SIZELESS_P (t))
+	{
+	  if (DECL_NAME (x))
+	    error_at (DECL_SOURCE_LOCATION (x),
+		      "field %qD has sizeless type", x);
+	  else
+	    error_at (DECL_SOURCE_LOCATION (x),
+		      "unnamed field has sizeless type");
+	  TREE_TYPE (x) = error_mark_node;
+	}
+
       if (DECL_NAME (x)
 	  || RECORD_OR_UNION_TYPE_P (TREE_TYPE (x)))
 	saw_named_field = true;
@@ -8262,14 +8306,14 @@ finish_struct (location_t loc, tree t, t
       }
   }
 
-  /* Note: C_TYPE_INCOMPLETE_VARS overloads TYPE_VFIELD which is used
+  /* Note: C_TYPE_INDEFINITE_VARS overloads TYPE_VFIELD which is used
      in dwarf2out via rest_of_decl_compilation below and means
      something totally different.  Since we will be clearing
-     C_TYPE_INCOMPLETE_VARS shortly after we iterate through them,
+     C_TYPE_INDEFINITE_VARS shortly after we iterate through them,
      clear it ahead of time and avoid problems in dwarf2out.  Ideally,
-     C_TYPE_INCOMPLETE_VARS should use some language specific
+     C_TYPE_INDEFINITE_VARS should use some language specific
      node.  */
-  tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
+  tree indefinite_vars = C_TYPE_INDEFINITE_VARS (TYPE_MAIN_VARIANT (t));
   for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
     {
       TYPE_FIELDS (x) = TYPE_FIELDS (t);
@@ -8277,7 +8321,7 @@ finish_struct (location_t loc, tree t, t
       C_TYPE_FIELDS_READONLY (x) = C_TYPE_FIELDS_READONLY (t);
       C_TYPE_FIELDS_VOLATILE (x) = C_TYPE_FIELDS_VOLATILE (t);
       C_TYPE_VARIABLE_SIZE (x) = C_TYPE_VARIABLE_SIZE (t);
-      C_TYPE_INCOMPLETE_VARS (x) = NULL_TREE;
+      C_TYPE_INDEFINITE_VARS (x) = NULL_TREE;
     }
 
   /* If this was supposed to be a transparent union, but we can't
@@ -8300,7 +8344,7 @@ finish_struct (location_t loc, tree t, t
 
   /* If this structure or union completes the type of any previous
      variable declaration, lay it out and output its rtl.  */
-  for (x = incomplete_vars; x; x = TREE_CHAIN (x))
+  for (x = indefinite_vars; x; x = TREE_CHAIN (x))
     {
       tree decl = TREE_VALUE (x);
       if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
@@ -8413,7 +8457,7 @@ start_enum (location_t loc, struct c_enu
      forward reference.  */
 
   if (name != NULL_TREE)
-    enumtype = lookup_tag (ENUMERAL_TYPE, name, true, &enumloc);
+    enumtype = lookup_tag (ENUMERAL_TYPE, false, name, true, &enumloc);
 
   if (enumtype == NULL_TREE || TREE_CODE (enumtype) != ENUMERAL_TYPE)
     {
@@ -8788,7 +8832,7 @@ start_function (struct c_declspecs *decl
 
   announce_function (decl1);
 
-  if (!COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl1))))
+  if (!DEFINITE_OR_VOID_TYPE_P (TREE_TYPE (TREE_TYPE (decl1))))
     {
       error_at (loc, "return type is an incomplete type");
       /* Make it return void instead.  */
@@ -9141,7 +9185,7 @@ store_parm_decls_oldstyle (tree fndecl,
 	continue;
 
       if (TREE_TYPE (parm) != error_mark_node
-	  && !COMPLETE_TYPE_P (TREE_TYPE (parm)))
+	  && !DEFINITE_TYPE_P (TREE_TYPE (parm)))
 	{
 	  error_at (DECL_SOURCE_LOCATION (parm),
 		    "parameter %qD has incomplete type", parm);
Index: gcc/c/c-parser.c
===================================================================
--- gcc/c/c-parser.c	2018-10-05 13:46:08.319810858 +0100
+++ gcc/c/c-parser.c	2018-10-15 14:13:32.996230178 +0100
@@ -2761,6 +2761,7 @@ c_parser_declspecs (c_parser *parser, st
 	  declspecs_add_type (loc, specs, t);
 	  break;
 	case RID_STRUCT:
+	case RID_SIZELESS_STRUCT:
 	case RID_UNION:
 	  if (!typespec_ok)
 	    goto out;
@@ -3023,7 +3024,7 @@ c_parser_enum_specifier (c_parser *parse
       ret.expr_const_operands = true;
       return ret;
     }
-  ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident);
+  ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, false, ident);
   /* In ISO C, enumerated types can be referred to only if already
      defined.  */
   if (pedantic && !COMPLETE_TYPE_P (ret.spec))
@@ -3083,8 +3084,12 @@ c_parser_struct_or_union_specifier (c_pa
   location_t struct_loc;
   location_t ident_loc = UNKNOWN_LOCATION;
   enum tree_code code;
+  bool sizeless_p = false;
   switch (c_parser_peek_token (parser)->keyword)
     {
+    case RID_SIZELESS_STRUCT:
+      sizeless_p = true;
+      /* Fall through.  */
     case RID_STRUCT:
       code = RECORD_TYPE;
       break;
@@ -3113,7 +3118,8 @@ c_parser_struct_or_union_specifier (c_pa
       /* Parse a struct or union definition.  Start the scope of the
 	 tag before parsing components.  */
       struct c_struct_parse_info *struct_info;
-      tree type = start_struct (struct_loc, code, ident, &struct_info);
+      tree type = start_struct (struct_loc, code, sizeless_p, ident,
+				&struct_info);
       tree postfix_attrs;
       /* We chain the components in reverse order, then put them in
 	 forward order at the end.  Each struct-declaration may
@@ -3230,7 +3236,7 @@ c_parser_struct_or_union_specifier (c_pa
       ret.expr_const_operands = true;
       return ret;
     }
-  ret = parser_xref_tag (ident_loc, code, ident);
+  ret = parser_xref_tag (ident_loc, code, sizeless_p, ident);
   return ret;
 }
 
@@ -7615,7 +7621,7 @@ c_parser_generic_selection (c_parser *pa
 	  if (TREE_CODE (assoc.type) == FUNCTION_TYPE)
 	    error_at (assoc.type_location,
 		      "%<_Generic%> association has function type");
-	  else if (!COMPLETE_TYPE_P (assoc.type))
+	  else if (!DEFINITE_TYPE_P (assoc.type))
 	    error_at (assoc.type_location,
 		      "%<_Generic%> association has incomplete type");
 
@@ -10536,6 +10542,10 @@ c_parser_objc_selector (c_parser *parser
     {
     case RID_ENUM:
     case RID_STRUCT:
+#if 0
+    /* Deliberately excluded from ObjC support.  */
+    case RID_SIZELESS_STRUCT:
+#endif
     case RID_UNION:
     case RID_IF:
     case RID_ELSE:
@@ -18277,6 +18287,8 @@ c_parser_omp_threadprivate (c_parser *pa
 	error_at (loc, "automatic variable %qE cannot be %<threadprivate%>", v);
       else if (TREE_TYPE (v) == error_mark_node)
 	;
+      /* For now we continue to require sized types here, so test
+	 COMPLETE_TYPE_P rather than DEFINITE_TYPE_P.  */
       else if (! COMPLETE_TYPE_P (TREE_TYPE (v)))
 	error_at (loc, "%<threadprivate%> %qE has incomplete type", v);
       else
Index: gcc/c/c-typeck.c
===================================================================
--- gcc/c/c-typeck.c	2018-10-05 13:46:08.319810858 +0100
+++ gcc/c/c-typeck.c	2018-10-15 14:13:32.996230178 +0100
@@ -189,10 +189,11 @@ static void free_all_tagged_tu_seen_up_t
 
 /* Do `exp = require_complete_type (loc, exp);' to make sure exp
    does not have an incomplete type.  (That includes void types.)
-   LOC is the location of the use.  */
+   LOC is the location of the use.  ALLOW_SIZELESS_P is true if
+   fully-defined sizeless types are OK.  */
 
 tree
-require_complete_type (location_t loc, tree value)
+require_complete_type (location_t loc, tree value, bool allow_sizeless_p)
 {
   tree type = TREE_TYPE (value);
 
@@ -200,7 +201,7 @@ require_complete_type (location_t loc, t
     return error_mark_node;
 
   /* First, detect a valid value with a complete type.  */
-  if (COMPLETE_TYPE_P (type))
+  if (allow_sizeless_p ? DEFINITE_TYPE_P (type) : COMPLETE_TYPE_P (type))
     return value;
 
   c_incomplete_type_error (loc, value, type);
@@ -2176,7 +2177,7 @@ default_conversion (tree exp)
       return error_mark_node;
     }
 
-  exp = require_complete_type (EXPR_LOC_OR_LOC (exp, input_location), exp);
+  exp = require_definite_type (EXPR_LOC_OR_LOC (exp, input_location), exp);
   if (exp == error_mark_node)
     return error_mark_node;
 
@@ -2398,7 +2399,7 @@ build_component_ref (location_t loc, tre
 
   if (code == RECORD_TYPE || code == UNION_TYPE)
     {
-      if (!COMPLETE_TYPE_P (type))
+      if (!DEFINITE_TYPE_P (type))
 	{
 	  c_incomplete_type_error (loc, NULL_TREE, type);
 	  return error_mark_node;
@@ -2548,7 +2549,7 @@ build_indirect_ref (location_t loc, tree
 
 	  ref = build1 (INDIRECT_REF, t, pointer);
 
-	  if (!COMPLETE_OR_VOID_TYPE_P (t) && TREE_CODE (t) != ARRAY_TYPE)
+	  if (!DEFINITE_OR_VOID_TYPE_P (t) && TREE_CODE (t) != ARRAY_TYPE)
 	    {
 	      if (!C_TYPE_ERROR_REPORTED (TREE_TYPE (ptr)))
 		{
@@ -3156,7 +3157,7 @@ build_function_call_vec (location_t loc,
 		 "function with qualified void return type called");
       return result;
     }
-  return require_complete_type (loc, result);
+  return require_definite_type (loc, result);
 }
 
 /* Like build_function_call_vec, but call also resolve_overloaded_builtin.  */
@@ -3312,7 +3313,7 @@ convert_arguments (location_t loc, vec<l
       val = c_fully_fold (val, false, NULL);
       STRIP_TYPE_NOPS (val);
 
-      val = require_complete_type (ploc, val);
+      val = require_definite_type (ploc, val);
 
       /* Some floating-point arguments must be promoted to double when
 	 no type is specified by a prototype.  This applies to
@@ -3342,7 +3343,7 @@ convert_arguments (location_t loc, vec<l
 	{
 	  /* Formal parm type is specified by a function prototype.  */
 
-	  if (type == error_mark_node || !COMPLETE_TYPE_P (type))
+	  if (type == error_mark_node || !DEFINITE_TYPE_P (type))
 	    {
 	      error_at (ploc, "type of formal parameter %d is incomplete",
 			parmnum + 1);
@@ -4230,7 +4231,7 @@ build_unary_op (location_t location, enu
     arg = remove_c_maybe_const_expr (arg);
 
   if (code != ADDR_EXPR)
-    arg = require_complete_type (location, arg);
+    arg = require_definite_type (location, arg);
 
   typecode = TREE_CODE (TREE_TYPE (arg));
   if (typecode == ERROR_MARK)
@@ -5596,7 +5597,7 @@ build_c_cast (location_t loc, tree type,
 
   if (!VOID_TYPE_P (type))
     {
-      value = require_complete_type (loc, value);
+      value = require_definite_type (loc, value);
       if (value == error_mark_node)
 	return error_mark_node;
     }
@@ -5876,7 +5877,7 @@ build_modify_expr (location_t location,
   bool is_atomic_op;
 
   /* Types that aren't fully specified cannot be used in assignments.  */
-  lhs = require_complete_type (location, lhs);
+  lhs = require_definite_type (location, lhs);
 
   /* Avoid duplicate error messages from operands that had errors.  */
   if (TREE_CODE (lhs) == ERROR_MARK || TREE_CODE (rhs) == ERROR_MARK)
@@ -6553,7 +6554,7 @@ #define WARNING_FOR_QUALIFIERS(LOCATION,
       error_at (location, "void value not ignored as it ought to be");
       return error_mark_node;
     }
-  rhs = require_complete_type (location, rhs);
+  rhs = require_definite_type (location, rhs);
   if (rhs == error_mark_node)
     return error_mark_node;
 
@@ -10785,7 +10786,7 @@ c_process_expr_stmt (location_t loc, tre
     verify_sequence_points (expr);
 
   if (TREE_TYPE (expr) != error_mark_node
-      && !COMPLETE_OR_VOID_TYPE_P (TREE_TYPE (expr))
+      && !DEFINITE_OR_VOID_TYPE_P (TREE_TYPE (expr))
       && TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
     error_at (loc, "expression statement has incomplete type");
 
@@ -14367,7 +14368,7 @@ c_build_qualified_type (tree type, int t
      type main variant.  */
   if (RECORD_OR_UNION_TYPE_P (var_type)
       && TYPE_MAIN_VARIANT (var_type) != var_type)
-    C_TYPE_INCOMPLETE_VARS (var_type) = 0;
+    C_TYPE_INDEFINITE_VARS (var_type) = 0;
   return var_type;
 }
 
@@ -14386,7 +14387,7 @@ c_build_va_arg (location_t loc1, tree ex
       error_at (loc1, "cannot use %<va_arg%> with reverse storage order");
       return error_mark_node;
     }
-  else if (!COMPLETE_TYPE_P (type))
+  else if (!DEFINITE_TYPE_P (type))
     {
       error_at (loc2, "second argument to %<va_arg%> is of incomplete "
 		"type %qT", type);
Index: gcc/objc/objc-runtime-shared-support.c
===================================================================
--- gcc/objc/objc-runtime-shared-support.c	2018-05-02 08:37:33.373752352 +0100
+++ gcc/objc/objc-runtime-shared-support.c	2018-10-15 14:13:32.996230178 +0100
@@ -69,7 +69,8 @@ objc_start_struct (tree name)
 {
   gcc_assert (!objc_building_struct);
   objc_building_struct = true;
-  return start_struct (input_location, RECORD_TYPE, name, &objc_struct_info);
+  return start_struct (input_location, RECORD_TYPE, false, name,
+		       &objc_struct_info);
 }
 
 /* Finish building a struct for objc.  */
Index: gcc/objcp/objcp-decl.h
===================================================================
--- gcc/objcp/objcp-decl.h	2018-05-02 08:37:54.209555320 +0100
+++ gcc/objcp/objcp-decl.h	2018-10-15 14:13:32.996230178 +0100
@@ -37,7 +37,7 @@ extern tree objcp_end_compound_stmt (tre
    invoke the original C++ functions if needed).  */
 #ifdef OBJCP_REMAP_FUNCTIONS
 
-#define start_struct(loc, code, name, struct_info) \
+#define start_struct(loc, code, sizeless_p, name, struct_info) \
 	objcp_start_struct (loc, code, name)
 #define finish_struct(loc, t, fieldlist, attributes, struct_info) \
 	objcp_finish_struct (loc, t, fieldlist, attributes)
Index: gcc/testsuite/gcc.dg/sizeless-1.c
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/gcc.dg/sizeless-1.c	2018-10-15 14:13:32.996230178 +0100
@@ -0,0 +1,269 @@
+/* { dg-options "-std=gnu99" } */
+
+struct initially_struct;
+__sizeless_struct initially_struct; /* { dg-error {'initially_struct' defined as wrong kind of tag} } */
+
+union initially_union;
+__sizeless_struct initially_union; /* { dg-error {'initially_union' defined as wrong kind of tag} } */
+
+enum initially_enum { AN_ENUM_VALUE };
+__sizeless_struct initially_enum; /* { dg-error {'initially_enum' defined as wrong kind of tag} } */
+
+__sizeless_struct initially_sizeless;
+struct initially_sizeless; /* { dg-error {'initially_sizeless' defined as wrong kind of tag} } */
+union initially_sizeless; /* { dg-error {'initially_sizeless' defined as wrong kind of tag} } */
+enum initially_sizeless { ANOTHER_ENUM_VALUE }; /* { dg-error {'initially_sizeless' defined as wrong kind of tag} } */
+
+typedef __sizeless_struct { int a; } ta;
+typedef __sizeless_struct { int a; } tb;
+typedef __sizeless_struct { int a, b; } tc;
+
+typedef __sizeless_struct ta_wrapper { ta a1, a2; } ta_wrapper;
+
+__sizeless_struct struct_in_sizeless {
+  struct { ta a; }; /* { dg-error {field 'a' has sizeless type} } */
+  struct { int b; };
+};
+__sizeless_struct union_in_sizeless {
+  union { ta a; }; /* { dg-error {field 'a' has sizeless type} } */
+  union { int b; };
+};
+__sizeless_struct sizeless_in_sizeless {
+  __sizeless_struct { ta a; };
+};
+
+__sizeless_struct flexible_in_sizeless {
+  int i;
+  int x[]; /* { dg-error {flexible array member in sizeless struct} } */
+};
+
+struct sizeless_in_struct {
+  __sizeless_struct { ta a; }; /* { dg-error {unnamed field has sizeless type} } */
+  __sizeless_struct { int b; }; /* { dg-error {unnamed field has sizeless type} } */
+};
+union sizeless_in_union {
+  __sizeless_struct { ta a; }; /* { dg-error {unnamed field has sizeless type} } */
+  __sizeless_struct { int b; }; /* { dg-error {unnamed field has sizeless type} } */
+};
+
+/* Sizeless objects with global scope.  */
+
+ta global_ta; /* { dg-error {sizeless variable 'global_ta' cannot have static storage duration} } */
+static ta local_ta; /* { dg-error {sizeless variable 'local_ta' cannot have static storage duration} } */
+extern ta extern_ta; /* { dg-error {sizeless variable 'extern_ta' cannot have static storage duration} } */
+__thread ta tls_ta; /* { dg-error {sizeless variable 'tls_ta' cannot have thread-local storage duration} } */
+_Atomic ta atomic_ta; /* { dg-error {sizeless variable 'atomic_ta' cannot have static storage duration} } */
+
+/* Sizeless arrays.  */
+
+typedef ta array_type[2]; /* { dg-error {array type has incomplete element type 'ta'} } */
+extern ta extern_array[]; /* { dg-error {array type has incomplete element type 'ta'} } */
+
+/* Sizeless fields.  */
+
+struct struct1 {
+  ta a; /* { dg-error {field 'a' has sizeless type} } */
+};
+
+union union1 {
+  ta a; /* { dg-error {field 'a' has sizeless type} } */
+};
+
+/* Pointers to sizeless types.  */
+
+ta *global_ta_ptr;
+
+/* Sizeless arguments and return values.  */
+
+void ext_consume_ta (ta);
+void ext_consume_varargs (int, ...);
+ta ext_produce_ta ();
+
+/* Main tests for statements and expressions.  */
+
+void
+statements (int n)
+{
+  /* Local declarations.  */
+
+  ta ta1, ta2;
+  tb tb1;
+  tc tc1;
+  static ta local_static_ta; /* { dg-error {sizeless variable 'local_static_ta' cannot have static storage duration} } */
+  extern ta another_extern_ta; /* { dg-error {sizeless variable 'another_extern_ta' cannot have static storage duration} } */
+
+  /* Layout queries.  */
+
+  sizeof (ta); /* { dg-error {invalid application of 'sizeof' to incomplete type} } */
+  sizeof (ta1); /* { dg-error {invalid application of 'sizeof' to incomplete type} } */
+  sizeof (ext_produce_ta ()); /* { dg-error {invalid application of 'sizeof' to incomplete type} } */
+  _Alignof (ta); /* { dg-error {invalid application of '(_Alignof|__alignof__)' to incomplete type} } */
+  _Alignof (ta1); /* { dg-error {invalid application of '(_Alignof|__alignof__)' to incomplete type} } */
+  _Alignof (ext_produce_ta ()); /* { dg-error {invalid application of '(_Alignof|__alignof__)' to incomplete type} } */
+
+  /* Initialization.  */
+
+  ta init_ta1 = ta1;
+  ta init_ta2 = tb1; /* { dg-error {invalid initializer} } */
+  ta init_ta3 = {};
+  ta init_ta4 = { 1 };
+  ta init_ta5 = { n };
+  ta init_ta6 = { n, n }; /* { dg-warning {excess elements in struct initializer} } */
+
+  int initi_a = ta1; /* { dg-error {incompatible types when initializing type 'int' using type 'ta'} } */
+  int initi_b = { ta1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'ta'} } */
+
+  ta_wrapper init_wrapper1 = {};
+  ta_wrapper init_wrapper2 = { ta1 };
+  ta_wrapper init_wrapper3 = { ta1, ta1 };
+  ta_wrapper init_wrapper4 = { ta1, ta1, ta1 }; /* { dg-warning {excess elements in struct initializer} } */
+
+  /* Compound literals.  */
+
+  (ta) {};
+  (ta) { ta1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'ta'} } */
+  (ta) { 1 };
+  (ta) { 1, 2 }; /* { dg-warning {excess elements in struct initializer} } */
+  (tc) { 1, 2 };
+  (tc) { 1, 2, 3 }; /* { dg-warning {excess elements in struct initializer} } */
+
+  (int) { ta1 }; /* { dg-error {incompatible types when initializing type 'int' using type 'ta'} } */
+
+  /* Arrays.  */
+
+  ta array[2]; /* { dg-error {array type has incomplete element type 'ta'} } */
+  ta zero_length_array[0]; /* { dg-error {array type has incomplete element type 'ta'} } */
+  ta empty_init_array[] = {}; /* { dg-error {array type has incomplete element type 'ta'} } */
+			      /* { dg-error {empty scalar initializer} "" { target *-*-* } .-1 } */
+  typedef ta vla_type[n]; /* { dg-error {array type has incomplete element type 'ta'} } */
+
+  /* Assignment.  */
+
+  n = ta1; /* { dg-error {incompatible types when assigning to type 'int' from type 'ta'} } */
+
+  ta1 = 0; /* { dg-error {incompatible types when assigning to type 'ta'[^\n]* from type 'int'} } */
+  ta1 = ta2;
+  ta1 = tb1; /* { dg-error {incompatible types when assigning to type 'ta'[^\n]* from type 'tb'} } */
+
+  /* Casting.  */
+
+  (void) ta1;
+  (ta) ta1;
+  (tb) ta1; /* { dg-error {conversion to non-scalar type requested} } */
+
+  /* Addressing and dereferencing.  */
+
+  ta *ta_ptr = &ta1;
+  tb *tb_ptr = &ta1; /* { dg-warning {initialization of 'tb \*'[^\n]* from incompatible pointer} } */
+  ta1 = *ta_ptr;
+  ta1 = *tb_ptr; /* { dg-error {incompatible types when assigning to type 'ta'[^\n]* from type 'tb'} } */
+
+  /* Pointer arithmetic.  */
+
+  ++ta_ptr; /* { dg-error {increment of pointer to an incomplete type 'ta'} } */
+  --ta_ptr; /* { dg-error {decrement of pointer to an incomplete type 'ta'} } */
+  ta_ptr++; /* { dg-error {increment of pointer to an incomplete type 'ta'} } */
+  ta_ptr--; /* { dg-error {decrement of pointer to an incomplete type 'ta'} } */
+  ta_ptr += 0; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+  ta_ptr += 1; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+  ta_ptr -= 0; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+  ta_ptr -= 1; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+  ta_ptr - ta_ptr; /* { dg-error {arithmetic on pointer to an incomplete type} } */
+  ta1 = ta_ptr[0]; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+  ta1 = ta_ptr[1]; /* { dg-error {invalid use of incomplete typedef 'ta'} } */
+
+  /* Component accesses.  */
+
+  tc1.a = 1;
+  tc1.b = 1;
+  tc1.c = 1; /* { dg-error {'tc'[^\n]* has no member named 'c'} } */
+  ta_ptr->a = 1;
+  ta_ptr->b = 1; /* { dg-error {'ta'[^\n]* has no member named 'b'} } */
+  int *int_ptr1 = &tc1.a;
+  int *int_ptr2 = &tc1.b;
+  int *int_ptr3 = &ta_ptr->a;
+
+  /* Unary operators.  */
+
+  +ta1; /* { dg-error {wrong type argument to unary plus} } */
+  -ta1; /* { dg-error {wrong type argument to unary minus} } */
+  ~ta1; /* { dg-error {wrong type argument to bit-complement} } */
+  !ta1; /* { dg-error {wrong type argument to unary exclamation mark} } */
+  *ta1; /* { dg-error {invalid type argument of unary '\*'} } */
+  __real ta1; /* { dg-error {wrong type argument to __real} } */
+  __imag ta1; /* { dg-error {wrong type argument to __imag} } */
+  ++ta1; /* { dg-error {wrong type argument to increment} } */
+  --ta1; /* { dg-error {wrong type argument to decrement} } */
+  ta1++; /* { dg-error {wrong type argument to increment} } */
+  ta1--; /* { dg-error {wrong type argument to decrement} } */
+
+  /* Conditional expressions.  */
+
+  ta1 ? 0 : 0; /* { dg-error {used struct type value where scalar is required} } */
+  0 ? ta1 : ta1;
+  0 ? ta1 : tb1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? ta1 : 0; /* { dg-error {type mismatch in conditional expression} } */
+  0 ? 0 : ta1; /* { dg-error {type mismatch in conditional expression} } */
+  0 ?: ta1; /* { dg-error {type mismatch in conditional expression} } */
+
+  /* Generic associations.  */
+
+  _Generic (ta1, default: 100);
+  _Generic (1, ta: 10, default: 20);
+
+  /* Function arguments.  */
+
+  ext_consume_ta (ta1);
+  ext_consume_ta (tb1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_ta'} } */
+  ext_consume_varargs (ta1); /* { dg-error {incompatible type for argument 1 of 'ext_consume_varargs'} } */
+  ext_consume_varargs (1, ta1);
+
+  /* Function returns.  */
+
+  ext_produce_ta ();
+  ta1 = ext_produce_ta ();
+  tb1 = ext_produce_ta (); /* { dg-error {incompatible types when assigning to type 'tb'[^\n]* from type 'ta'} } */
+
+  /* Varargs processing.  */
+
+  __builtin_va_list valist;
+  __builtin_va_arg (valist, ta);
+
+  /* Statement expressions.  */
+
+  ({ ta1; });
+  ({ ta another_ta = *ta_ptr; another_ta; });
+
+  /* Use in atomics.  */
+
+  __sync_lock_test_and_set (global_ta_ptr, 0); /* { dg-error {operand type '[^']*'[^\n]* is incompatible with argument 1} } */
+}
+
+/* Function parameters in definitions.  */
+
+void
+old_style (input_ta)
+     ta input_ta;
+{
+  ta ta1 = input_ta;
+}
+
+void
+new_style_param (ta input_ta)
+{
+  ta ta1 = input_ta;
+}
+
+/* Function return values in definitions in definitions.  */
+
+ta
+good_return_ta (ta param)
+{
+  return param;
+}
+
+ta
+bad_return_ta (tb param)
+{
+  return param; /* { dg-error {incompatible types when returning type 'tb'[^\n]* but 'ta'[^\n]* was expected} } */
+}



[-- Attachment #2: sizeless-types-in-c.tar.gz --]
[-- Type: application/gzip, Size: 19240 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [10/10] C++ support for sizeless types
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (8 preceding siblings ...)
  2018-10-15 14:50 ` [09/10] C support for sizeless types Richard Sandiford
@ 2018-10-15 15:01 ` Richard Sandiford
  2018-10-15 15:14 ` [00/10][RFC] Splitting the C and C++ concept of "complete type" Joseph Myers
  2018-10-15 18:57 ` Uecker, Martin
  11 siblings, 0 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-15 15:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: joseph, jason, nathan, nd

[-- Attachment #1: Type: text/plain, Size: 126589 bytes --]

This patch adds support for sizeless types to C++, along the lines
described in the covering RFC.  As with the C patch, it's actually a
squash of a series of patches that I've attached as a tarball, with each
patch building up the support piece-by-piece.  The individual patches
have covering notes giving a quick explanation.

Most of the patch is replacing the test for whether a class has been
fully defined (for COMPLETE_TYPE_P) or whether the definition has
been started (COMPLETE_OR_OPEN_TYPE_P) in cases where member lookup
is all that matters.

The patch adds comments explaining why some tests of COMPLETE_TYPE_P
are either correct as-is or why it doesn't matter whether they're
replaced or not.  These were mostly for keeping track while doing
the work; I don't know whether it would make sense to commit them.

I realise this isn't likely to be acceptable in its current state,
not least because it would create a maintenance burden as the language
evolves.  The question is more: what changes would be needed to
make it acceptable?

One option would be to make COMPLETE_TYPE_P mean "the type is fully
defined" and use something else for "the type is fully defined and
has a defined size, alignment and layout".  The problem is that we'll
then err on the side of accepting things for sizeless types rather than
rejecting them, whereas the idea was to make the handling for complete
types conservatively correct.  In other words, the language meaning of
complete type would still be "has sufficient information to determine
the size" while COMPLETE_TYPE_P would mean something else.

Another option would be to remove COMPLETE_TYPE_P altogether and
use a new macro for "is fully defined and has a measurable size".
E.g. we could have:

    TYPE_DEFINITION_STARTED_P (etc.)
    --> what's now COMPLETE_OR_OPEN_TYPE_P

    TYPE_DEFINED_P (or TYPE_FULLY_DEFINED_P, etc.)
    --> what's now COMPLETE_TYPE_P when the size, alignment and
    layout don't matter

    TYPE_SIZE_KNOWN_P (or TYPE_SIZE_DEFINED_P, TYPE_SIZE_COMPLETE_P, etc.)
    --> what's now COMPLETE_TYPE_P when the size, alignment or
    layout does matter.

2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (definite_or_array_type_p): New function.

gcc/cp/
	* cp-tree.h (COMPLETE_OR_OPEN_TYPE_P): Replace with...
	(DEFINITE_OR_OPEN_TYPE_P): ...this new macro.
	(sizeless_struct_type): New tag_types enum.
	(require_complete_type_sfinae): Add a default false parameter.
	(complete_type_or_else): Likewise.
	(complete_type_or_maybe_complain): Likewise.
	(require_definite_type_sfinae): New function.
	(definite_type_or_maybe_complain): Likewise.
	(definite_type_or_else): Likewise.
	* call.c (reference_binding): Require definite types rather
	than complete types when processing the target type of a
	reference binding.
	(implicit_conversion): Likewise when processing the target
	type of an implicit conversion.
	(convert_arg_to_ellipsis): Likewise when processing a value
	that is being passed through "...".
	(build_x_va_arg): Likewise when processing the second argument
	to va_arg.
	(maybe_warn_class_memaccess): Likewise when processing a
	member access.
	(build_cxx_call): Likewise when processing the function in
	a function call.
	(build_special_member_call): Likewise when looking up a
	special member.
	(complain_about_no_candidates_for_method_call): Likewise
	when reporting that a type has no suitable member functions.
	(add_builtin_candidate, build_op_delete_call)
	(convert_like_real): Add comments.
	* class.c (build_base_path, check_bases): Likewise.
	(check_field_decls, build_base_field): Likewise.
	(add_method): Require definite types rather than complete types
	when adding a new member to a class.
	(type_has_virtual_destructor): Relax the assert so that it
	accepts sizeless definite types as well as complete types.
	(type_build_dtor_call): Handle destructors in sizeless definite
	types in the same way as destructors in complete classes.
	(finish_struct_1): Report a redeclaration error for classes
	that are already definite as well as already complete.
	Record that sizeless types are not literal types.
	(is_really_empty_class): Use the cached result for definite
	classes as well as complete classes.
	* constexpr.c (literal_type_p): Relax the assert so that it
	accepts sizeless definite types as well as complete types.
	(ensure_literal_type_for_constexpr_object): Check for indefinite
	rather than incomplete types when short-circuiting the test
	for literal types.
	(cxx_eval_constant_expression): Add comments.
	(potential_constant_expression_1): Likewise.
	* cp-gimplify.c (cxx_omp_finish_clause): Likewise.
	(cp_fold): Check for definite rather than complete types when
	handling the offsetof-like idiom.
	* cvt.c (cp_convert_to_pointer): Require definite types rather than
	complete types when processing the source of a pointer conversion.
	(convert_to_void): Check for indefinite types rather than incomplete
	types when issuing the warning abut converting to void.
	(build_expr_type_conversion): Add a comment.
	* decl.c (redeclaration_error_message): Check for definite types
	rather than complete types when deciding whether to issue a
	redeclaration error.
	(make_typename_type): Require definite types rather than complete
	types when resolving a typename.
	(cp_finish_decomp): Likewise when processing the source of a
	structured binding.
	(require_complete_types_for_parms): Likewise when processing
	the types of formal parameters.
	(check_function_type): Likewise when processing the return type
	in a function definition.
	(fixup_anonymous_aggr): Reject anonymous sizeless structures
	in sized aggregates.
	(start_decl): Check for definite types rather than complete types
	when processing a decl that belongs to an already-defined class.
	(start_decl_1): Require initialized and aggregate definitions
	to have definite rather than complete type.  Reject sizeless decls
	with static or thread-local storage duration.
	(layout_var_decl): Check for definite types rather than complete types
	when deciding whether to lay out a decl.
	(check_array_initializer, get_tuple_size): Add comments.
	(check_static_variable_definition, grok_op_properties): Likewise.
	(check_initializer): Require the initialized type to have definite
	rather than complete type.
	(cp_finish_decl): Check for indefinite types rather than incomplete
	types when deciding whether to suppress debug information.
	(grokdeclarator): Likewise when processing qualified function names.
	Reject virtual methods inside a sizeless struct.  Accept sizeless
	definite members in sizeless structs, but not in normal sized
	aggregates.
	(tag_name, xref_tag_1): Handle sizeless_struct_type.
	(xref_basetypes): Reject base classes for sizeless structs.
	(finish_enum_value_list): Check for definite types rather than
	complete types when processing qualified enum names.
	(begin_destructor_body): Likewise when deciding whether to build
	a destructor body.
	(maybe_register_incomplete_var): Likewise when deciding whether
	to queue a variable for later processing.
	* decl2.c (check_classfn): Check for definite types rather than
	complete types when deciding how to report a name lookup failure.
	(cp_omp_mappable_type): Add a comment.
	* error.c (class_key_or_enum_as_string): Return "__sizeless_struct"
	for sizeless structs.
	(qualified_name_lookup_error): Check for definite types rather than
	complete types when deciding how to report a name lookup failure.
	* except.c (complete_ptr_ref_or_void_ptr_p): Add comments.
	* expr.c (cplus_expand_constant): Check for definite types rather than
	complete types when deciding whether to punt on PTRMEM_CSTs.
	* friend.c (do_friend): Require definite types rather than complete
	types when processing the class name in a member friend declaration.
	* init.c (build_value_init_noctor): Likewise when processing
	a vector initializer.
	(build_offset_ref): Likewise when processing the class name
	in a member reference.  Reject attempts to create pointers
	to member variables of sizeless structures.
	(emit_mem_initializers): Check for definite types rather than
	complete types when deciding whether to emit code for initializers.
	(build_new): Add a comment.
	(build_vec_delete_1): Reject attempts to delete sizeless objects.
	(build_delete): Likewise.
	* lambda.c (lambda_function): Check for definite types rather than
	complete types when trying to detect debug_tree calls.
	(lambda_expr_this_capture): Likewise when handling "this" in a NSDMI.
	(add_capture): Tighten assert so that it requires the capture
	to be indefinite as well as incomplete.
	* name-lookup.c (search_anon_aggr): Accept definite types as
	well as complete types.
	(get_class_binding_direct, get_class_binding, find_member_slot)
	(add_member_slot): Extend handling of name lookup and member
	registration for complete types to sizeless types.
	(maybe_process_template_type_declaration, do_pushtag): Check for
	indefinite types rather than incomplete types when deciding
	whether to queue a template instantiation.
	* parser.c (cp_keyword_starts_decl_specifier_p): Handle
	RID_SIZELESS_STRUCT.
	(cp_parser_type_specifier): Likewise.
	(cp_parser_token_is_class_key): Likewise.
	(cp_parser_diagnose_invalid_type_name): Check for definite types
	rather than complete types when deciding how to report a name
	lookup failure.
	(cp_parser_nested_name_specifier_opt): Extend shortcut for
	complete types to definite types.
	(cp_parser_postfix_dot_deref_expression): Require definite types
	rather than complete types when processing "." and "->" expressions.
	(cp_parser_perform_range_for_lookup): Likewise when processing
	range-based for loops.
	(cp_parser_class_head): Check whether the existing type is definite
	rather than complete when deciding whether to issue a redefinition
	error.
	(cp_parser_check_class_key): Extend checks for invalid tags to
	include __sizeless_struct.
	* pt.c (maybe_new_partial_specialization): Check for indefinite types
	rather than incomplete types when deciding whether to exit early.
	(maybe_process_partial_specialization): Likewise when deciding
	whether something is an explicit specialization.
	(process_partial_specialization): Tighten assert so that it requires
	the instantiation to be indefinite as well as incomplete.  Check for
	definite types rather than complete types when checking whether
	earlier uses are ambiguated or invalidated by the specialization.
	(lookup_template_class_1): Copy TYPE_SIZELESS_P to the instantiation.
	(tsubst_friend_function): Require definite types rather than complete
	types when processing the class name in a member friend declaration.
	(tsubst): Likewise when processing the class name in a typename.
	(do_type_instantiation): Likewise for the type of a template
	when processing an instantiation of it.
	(can_complete_type_without_circularity): Check for definite types
	rather than complete types when deciding whether to exit early.
	(instantiate_class_template_1): Likewise, and also when deciding
	whether to defer the instantiation till later.  Allow instantations
	of sizeless structure templates to have sizeless members.
	(instantiate_pending_templates): Check for definite types rather than
	complete types when deciding whether to process or defer a queued
	instantiation.
	* rtti.c (get_tinfo_decl_dynamic, typeid_ok_p, get_typeid)
	(build_dynamic_cast_1, tinfo_base_init): Add comments.
	* search.c (lookup_base): Extend base lookup in complete types to
	definite types.
	* semantics.c (finish_underlying_type, calculate_direct_bases)
	(calculate_bases, finish_offsetof, finish_omp_clauses)
	(finish_omp_threadprivate): Add comments.
	(check_trait_type): Require definite types rather than complete
	types when processing a type trait query.
	(finish_trait_expr): Likewise for the second type in __is_base_of.
	(apply_deduced_return_type): Likewise when processing an auto
	return type.
	* tree.c (build_cplus_new): Likewise when processing a constructor
	call.
	(build_cplus_array_type): Add a comment.
	(strip_typedefs): Check for definite types rather than complete
	types when deciding whether to skip the attribute handling.
	(type_has_nontrivial_copy_init): Accept definite types as
	well as complete types.
	(record_has_unique_obj_representations): Handle poly_int offsets.
	* typeck.c (require_complete_type_sfinae): Add a sizeless_p
	parameter.
	(complete_type_or_maybe_complain): Likewise.
	(complete_type_or_else): Likewise.
	(complete_type): Check for definite types rather than complete
	types when deciding whether to bail out early.
	(cxx_sizeof_or_alignof_type, cxx_sizeof_nowarn): Add comments.
	(cp_build_array_ref, pointer_diff): Likewise.
	(build_reinterpret_cast_1, ptr_reasonably_similar): Likewise.
	(decay_conversion): Require definite types rather than complete
	types when processing the source of the conversion.
	(build_class_member_access_expr): Likewise when processing a
	member access.
	(finish_class_member_access_expr): Likewise.
	(convert_arguments): Likewise when processing the types of
	formal and actual parameters in a function call.
	(cp_build_modify_expr): Likewise when processing the lhs
	of an assignment.
	(convert_for_assignment): Likewise when processing the rhs
	of an assignment.
	(get_member_function_from_ptrfunc): Check for definite types
	rather than complete types when optimizing the handling of
	non-virtual methods.
	(build_x_unary_op): Likewise when deciding whether to circumvent
	the use of operator&.
	(cp_apply_type_quals_to_decl): Likewise when deciding whether
	to remove const qualification.
	* typeck2.c (complete_type_check_abstract): Accept definite types as
	well as complete types.
	(abstract_virtuals_error_sfinae): Check for indefinite types
	rather than incomplete types when deciding whether to postpone
	the check for whether a type has abstract virtual functions.
	(digest_init_r): Require definite types rather than complete
	types when processing the type being initialized.
	(build_functional_cast): Likewise when processing a constructor call.
	(build_m_component_ref, add_exception_specifier): Add a comment.
	(require_complete_eh_spec_types): Likewise.

gcc/testsuite/
	* g++.dg/ext/sizeless-1.C: New test.
	* g++.dg/ext/sizeless-2.C: Likewise.
	* g++.dg/ext/sizeless-3.C: Likewise.
	* g++.dg/ext/sizeless-4.C: Likewise.
	* g++.dg/ext/sizeless-5.C: Likewise.
	* g++.dg/ext/sizeless-6.C: Likewise.
	* g++.dg/ext/sizeless-7.C: Likewise.

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2018-10-15 14:13:32.988230244 +0100
+++ gcc/c-family/c-common.h	2018-10-15 14:13:37.844189990 +0100
@@ -774,6 +774,17 @@ complete_or_array_type_p (const_tree typ
 	     && COMPLETE_TYPE_P (TREE_TYPE (type)));
 }
 
+/* Return true if the argument is a definite type or an array
+   of unknown bound (whose type is incomplete but) whose elements
+   have definite (and complete) type.  */
+static inline bool
+definite_or_array_type_p (const_tree type)
+{
+  return (DEFINITE_TYPE_P (type)
+	  || (TREE_CODE (type) == ARRAY_TYPE
+	      && COMPLETE_TYPE_P (TREE_TYPE (type))));
+}
+
 struct visibility_flags
 {
   unsigned inpragma : 1;	/* True when in #pragma GCC visibility.  */
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	2018-10-15 14:08:45.570612130 +0100
+++ gcc/cp/cp-tree.h	2018-10-15 14:13:37.848189957 +0100
@@ -2185,10 +2185,10 @@ #define TYPE_HAS_ARRAY_NEW_OPERATOR(NODE
    starting the definition of this type has been seen.  */
 #define TYPE_BEING_DEFINED(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->being_defined)
 
-/* Nonzero means that this type is either complete or being defined, so we
+/* Nonzero means that this type is either definite or being defined, so we
    can do lookup in it.  */
-#define COMPLETE_OR_OPEN_TYPE_P(NODE) \
-  (COMPLETE_TYPE_P (NODE) || (CLASS_TYPE_P (NODE) && TYPE_BEING_DEFINED (NODE)))
+#define DEFINITE_OR_OPEN_TYPE_P(NODE) \
+  (DEFINITE_TYPE_P (NODE) || (CLASS_TYPE_P (NODE) && TYPE_BEING_DEFINED (NODE)))
 
 /* Mark bits for repeated base checks.  */
 #define TYPE_MARKED_P(NODE) TREE_LANG_FLAG_6 (TYPE_CHECK (NODE))
@@ -4992,6 +4992,7 @@ enum tag_types {
   none_type = 0, /* Not a tag type.  */
   record_type,   /* "struct" types.  */
   class_type,    /* "class" types.  */
+  sizeless_struct_type, /* "__sizeless_struct" types.  */
   union_type,    /* "union" types.  */
   enum_type,     /* "enum" types.  */
   typename_type, /* "typename" types.  */
@@ -7234,10 +7235,12 @@ extern int string_conv_p			(const_tree,
 extern tree cp_truthvalue_conversion		(tree);
 extern tree condition_conversion		(tree);
 extern tree require_complete_type		(tree);
-extern tree require_complete_type_sfinae	(tree, tsubst_flags_t);
+extern tree require_complete_type_sfinae	(tree, tsubst_flags_t,
+						 bool = false);
 extern tree complete_type			(tree);
-extern tree complete_type_or_else		(tree, tree);
-extern tree complete_type_or_maybe_complain	(tree, tree, tsubst_flags_t);
+extern tree complete_type_or_else		(tree, tree, bool = false);
+extern tree complete_type_or_maybe_complain	(tree, tree, tsubst_flags_t,
+						 bool = false);
 inline bool type_unknown_p			(const_tree);
 enum { ce_derived, ce_type, ce_normal, ce_exact };
 extern bool comp_except_specs			(const_tree, const_tree, int);
@@ -7654,6 +7657,39 @@ null_node_p (const_tree expr)
   return expr == null_node;
 }
 
+/* Do:
+
+     value = require_definite_type_sfinae (value, complain);
+
+   to make sure VALUE does not have an indefinite type.  (Void types are
+   always indefinite.)  Return error_mark_node if VALUE is not definite.  */
+
+inline tree
+require_definite_type_sfinae (tree value, tsubst_flags_t complain)
+{
+  return require_complete_type_sfinae (value, complain, true);
+}
+
+/* Like complete_type, but check whether the result is definite.  Return
+   NULL_TREE if the type doesn't meet this condition, and also report an
+   error if COMPLAIN allows.  VALUE is used for informative diagnostics.  */
+
+inline tree
+definite_type_or_maybe_complain (tree type, tree value,
+				 tsubst_flags_t complain)
+{
+  return complete_type_or_maybe_complain (type, value, complain, true);
+}
+
+/* Like complete_type, but report an error and return NULL_TREE if the
+   result isn't a definite type.  */
+
+inline tree
+definite_type_or_else (tree type, tree value)
+{
+  return complete_type_or_else (type, value, true);
+}
+
 #if CHECKING_P
 namespace selftest {
   extern void run_cp_tests (void);
Index: gcc/cp/call.c
===================================================================
--- gcc/cp/call.c	2018-10-15 14:08:45.570612130 +0100
+++ gcc/cp/call.c	2018-10-15 14:13:37.844189990 +0100
@@ -1745,7 +1745,7 @@ reference_binding (tree rto, tree rfrom,
      but if TO is an incomplete class, we need to reject this conversion
      now to avoid unnecessary instantiation.  */
   if (!CP_TYPE_CONST_NON_VOLATILE_P (to) && !TYPE_REF_IS_RVALUE (rto)
-      && !COMPLETE_TYPE_P (to))
+      && !DEFINITE_TYPE_P (to))
     return NULL;
 
   /* We're generating a temporary now, but don't bind any more in the
@@ -1845,7 +1845,7 @@ implicit_conversion (tree to, tree from,
   /* Call reshape_init early to remove redundant braces.  */
   if (expr && BRACE_ENCLOSED_INITIALIZER_P (expr)
       && CLASS_TYPE_P (to)
-      && COMPLETE_TYPE_P (complete_type (to))
+      && DEFINITE_TYPE_P (complete_type (to))
       && !CLASSTYPE_NON_AGGREGATE (to))
     {
       expr = reshape_init (to, expr, complain);
@@ -2606,6 +2606,8 @@ add_builtin_candidate (struct z_candidat
 
 	  if (MAYBE_CLASS_TYPE_P (c1) && DERIVED_FROM_P (c2, c1)
 	      && (TYPE_PTRMEMFUNC_P (type2)
+		  /* Correct for sizeless types since we don't allow
+		     pointers to member variables for them.  */
 		  || is_complete (TYPE_PTRMEM_POINTED_TO_TYPE (type2))))
 	    break;
 	}
@@ -6280,6 +6282,7 @@ build_op_delete_call (enum tree_code cod
   fnname = ovl_op_identifier (false, code);
 
   if (CLASS_TYPE_P (type)
+      /* OK for sizeless types, which can't be deleted.  */
       && COMPLETE_TYPE_P (complete_type (type))
       && !global_p)
     /* In [class.free]
@@ -6433,6 +6436,8 @@ build_op_delete_call (enum tree_code cod
 	       with a parameter of type std::size_t is selected.  */
 	    else
 	      {
+		/* Doesn't matter for sizeless types, since those can't
+		   be in arrays and can't be deleted.  */
 		want_size = COMPLETE_TYPE_P (type);
 		if (code == VEC_DELETE_EXPR
 		    && !TYPE_VEC_NEW_USES_COOKIE (type))
@@ -6981,6 +6986,8 @@ convert_like_real (conversion *convs, tr
 	/* Build up the initializer_list object.  Note: fail gracefully
 	   if the object cannot be completed because, for example, no
 	   definition is provided (c++/80956).  */
+	/* Not relevant for sizeless type; std::initializer_list can
+	   never be sizeless.  */
 	totype = complete_type_or_maybe_complain (totype, NULL_TREE, complain);
 	if (!totype)
 	  return error_mark_node;
@@ -7290,14 +7297,14 @@ convert_arg_to_ellipsis (tree arg, tsubs
 	arg = cp_perform_integral_promotions (arg, complain);
     }
 
-  arg = require_complete_type_sfinae (arg, complain);
+  arg = require_definite_type_sfinae (arg, complain);
   arg_type = TREE_TYPE (arg);
 
   if (arg != error_mark_node
       /* In a template (or ill-formed code), we can have an incomplete type
 	 even after require_complete_type_sfinae, in which case we don't know
 	 whether it has trivial copy or not.  */
-      && COMPLETE_TYPE_P (arg_type)
+      && DEFINITE_TYPE_P (arg_type)
       && !cp_unevaluated_operand)
     {
       /* [expr.call] 5.2.2/7:
@@ -7344,7 +7351,7 @@ build_x_va_arg (source_location loc, tre
       return r;
     }
 
-  type = complete_type_or_else (type, NULL_TREE);
+  type = definite_type_or_else (type, NULL_TREE);
 
   if (expr == error_mark_node || !type)
     return error_mark_node;
@@ -8650,7 +8657,7 @@ maybe_warn_class_memaccess (location_t l
      a complete class type.  */
   tree desttype = TREE_TYPE (TREE_TYPE (dest));
 
-  if (!desttype || !COMPLETE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
+  if (!desttype || !DEFINITE_TYPE_P (desttype) || !CLASS_TYPE_P (desttype))
     return;
 
   /* Check to see if the raw memory call is made by a non-static member
@@ -8934,7 +8941,7 @@ build_cxx_call (tree fn, int nargs, tree
      prvalue. The type of the prvalue may be incomplete.  */
   if (!(complain & tf_decltype))
     {
-      fn = require_complete_type_sfinae (fn, complain);
+      fn = require_definite_type_sfinae (fn, complain);
       if (fn == error_mark_node)
 	return error_mark_node;
 
@@ -9025,7 +9032,7 @@ build_special_member_call (tree instance
   if (TYPE_P (binfo))
     {
       /* Resolve the name.  */
-      if (!complete_type_or_maybe_complain (binfo, NULL_TREE, complain))
+      if (!definite_type_or_maybe_complain (binfo, NULL_TREE, complain))
 	return error_mark_node;
 
       binfo = TYPE_BINFO (binfo);
@@ -9276,7 +9283,7 @@ complain_about_no_candidates_for_method_
 					      vec<tree, va_gc> *user_args)
 {
   auto_diagnostic_group d;
-  if (!COMPLETE_OR_OPEN_TYPE_P (basetype))
+  if (!DEFINITE_OR_OPEN_TYPE_P (basetype))
     cxx_incomplete_type_error (instance, basetype);
   else if (optype)
     error ("no matching function for call to %<%T::operator %T(%A)%#V%>",
Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	2018-10-05 13:46:09.839798545 +0100
+++ gcc/cp/class.c	2018-10-15 14:13:37.848189957 +0100
@@ -369,6 +369,8 @@ build_base_path (enum tree_code code,
       goto indout;
     }
 
+  /* Shouldn't reach here for sizeless types, since they can't have base
+     classes or be used as base classes.  */
   if (!COMPLETE_TYPE_P (probe))
     {
       if (complain & tf_error)
@@ -1142,7 +1144,7 @@ add_method (tree type, tree method, bool
 
   current_fns = ovl_insert (method, current_fns, via_using);
 
-  if (!COMPLETE_TYPE_P (type) && !DECL_CONV_FN_P (method)
+  if (!DEFINITE_TYPE_P (type) && !DECL_CONV_FN_P (method)
       && !push_class_level_binding (DECL_NAME (method), current_fns))
     return false;
 
@@ -1642,6 +1644,7 @@ check_bases (tree t,
     {
       tree basetype = TREE_TYPE (base_binfo);
 
+      /* Correct for sizeless types, since they can never be base classes.  */
       gcc_assert (COMPLETE_TYPE_P (basetype));
 
       if (CLASSTYPE_FINAL (basetype))
@@ -3503,6 +3506,8 @@ check_field_decls (tree t, tree *access_
          class becomes non-literal.  Per Core/1453, volatile non-static
 	 data members and base classes are also not allowed.
 	 Note: if the type is incomplete we will complain later on.  */
+      /* Doesn't matter for sizeless types, since they're not literal
+	 to start with.  */
       if (COMPLETE_TYPE_P (type)
 	  && (!literal_type_p (type) || CP_TYPE_VOLATILE_P (type))) 
         CLASSTYPE_LITERAL_P (t) = false;
@@ -4307,6 +4312,7 @@ build_base_field (record_layout_info rli
   tree t = rli->t;
   tree basetype = BINFO_TYPE (binfo);
 
+  /* Correct for sizeless types, since they can never be base classes.  */
   if (!COMPLETE_TYPE_P (basetype))
     /* This error is now reported in xref_tag, thus giving better
        location information.  */
@@ -5182,7 +5188,7 @@ type_has_virtual_destructor (tree type)
   if (!CLASS_TYPE_P (type))
     return false;
 
-  gcc_assert (COMPLETE_TYPE_P (type));
+  gcc_assert (DEFINITE_TYPE_P (type));
   dtor = CLASSTYPE_DESTRUCTOR (type);
   return (dtor && DECL_VIRTUAL_P (dtor));
 }
@@ -5312,7 +5318,7 @@ type_build_dtor_call (tree t)
     return true;
   inner = strip_array_types (t);
   if (!CLASS_TYPE_P (inner) || ANON_AGGR_TYPE_P (inner)
-      || !COMPLETE_TYPE_P (inner))
+      || !DEFINITE_TYPE_P (inner))
     return false;
   if (cxx_dialect < cxx11)
     return false;
@@ -6892,7 +6898,7 @@ finish_struct_1 (tree t)
   /* A TREE_LIST.  The TREE_VALUE of each node is a FUNCTION_DECL.  */
   tree virtuals = NULL_TREE;
 
-  if (COMPLETE_TYPE_P (t))
+  if (DEFINITE_TYPE_P (t))
     {
       gcc_assert (MAYBE_CLASS_TYPE_P (t));
       error ("redefinition of %q#T", t);
@@ -6910,7 +6916,7 @@ finish_struct_1 (tree t)
   CLASSTYPE_EMPTY_P (t) = 1;
   CLASSTYPE_NEARLY_EMPTY_P (t) = 1;
   CLASSTYPE_CONTAINS_EMPTY_CLASS_P (t) = 0;
-  CLASSTYPE_LITERAL_P (t) = true;
+  CLASSTYPE_LITERAL_P (t) = !TYPE_SIZELESS_P (t);
 
   /* Do end-of-class semantic processing: checking the validity of the
      bases and members and add implicitly generated methods.  */
@@ -8291,7 +8297,7 @@ is_really_empty_class (tree type)
 
       /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
 	 out, but we'd like to be able to check this before then.  */
-      if (COMPLETE_TYPE_P (type) && is_empty_class (type))
+      if (DEFINITE_TYPE_P (type) && is_empty_class (type))
 	return true;
 
       for (binfo = TYPE_BINFO (type), i = 0;
Index: gcc/cp/constexpr.c
===================================================================
--- gcc/cp/constexpr.c	2018-10-15 14:08:45.562612196 +0100
+++ gcc/cp/constexpr.c	2018-10-15 14:13:37.848189957 +0100
@@ -66,7 +66,7 @@ literal_type_p (tree t)
   if (CLASS_TYPE_P (t))
     {
       t = complete_type (t);
-      gcc_assert (COMPLETE_TYPE_P (t) || errorcount);
+      gcc_assert (DEFINITE_TYPE_P (t) || errorcount);
       return CLASSTYPE_LITERAL_P (t);
     }
   if (TREE_CODE (t) == ARRAY_TYPE)
@@ -88,7 +88,7 @@ ensure_literal_type_for_constexpr_object
       && !processing_template_decl)
     {
       tree stype = strip_array_types (type);
-      if (CLASS_TYPE_P (stype) && !COMPLETE_TYPE_P (complete_type (stype)))
+      if (CLASS_TYPE_P (stype) && !DEFINITE_TYPE_P (complete_type (stype)))
 	/* Don't complain here, we'll complain about incompleteness
 	   when we try to initialize the variable.  */;
       else if (type_uses_auto (type))
@@ -4215,6 +4215,8 @@ cxx_eval_constant_expression (const cons
       /* is_really_empty_class doesn't take into account _vptr, so initializing
 	 otherwise empty class with { } would overwrite the initializer that
 	 initialize_vtable created for us.  */
+      /* Sizeless structs aren't ever constant expressions, even if they're
+	 empty.  */
       if (COMPLETE_TYPE_P (TREE_TYPE (t))
 	  && !TYPE_POLYMORPHIC_P (TREE_TYPE (t))
 	  && is_really_empty_class (TREE_TYPE (t)))
@@ -4264,7 +4266,9 @@ cxx_eval_constant_expression (const cons
 	/* Defer in case this is only used for its type.  */;
       else if (TYPE_REF_P (TREE_TYPE (t)))
 	/* Defer, there's no lvalue->rvalue conversion.  */;
-      else if (COMPLETE_TYPE_P (TREE_TYPE (t))
+      else if (/* Shouldn't matter for correctness what we do here for sizeless
+		  types, since they're always non-literal.  */
+	       COMPLETE_TYPE_P (TREE_TYPE (t))
 	       && is_really_empty_class (TREE_TYPE (t)))
 	{
 	  /* If the class is empty, we aren't actually loading anything.  */
@@ -5682,6 +5686,8 @@ #define RECUR(T,RV) \
 	      || !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (t))
 	      || (DECL_INITIAL (t)
 		  && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (t)))
+	  /* Shouldn't matter for correctness what we do here for sizeless
+	     types, since they're always non-literal.  */
 	  && COMPLETE_TYPE_P (TREE_TYPE (t))
 	  && !is_really_empty_class (TREE_TYPE (t)))
         {
@@ -6019,6 +6025,9 @@ #define RECUR(T,RV) \
 	 might be a constexpr user-defined conversion.  */
       else if (cxx_dialect >= cxx11
 	       && (dependent_type_p (TREE_TYPE (t))
+		   /* Shouldn't matter what we do here for sizeless types,
+		      since they're never literal types.  Any user-defined
+		      conversion would be an invalid constexpr.  */
 		   || !COMPLETE_TYPE_P (TREE_TYPE (t))
 		   || literal_type_p (TREE_TYPE (t)))
 	       && TREE_OPERAND (t, 0))
Index: gcc/cp/cp-gimplify.c
===================================================================
--- gcc/cp/cp-gimplify.c	2018-08-28 11:25:45.742884177 +0100
+++ gcc/cp/cp-gimplify.c	2018-10-15 14:13:37.848189957 +0100
@@ -2032,6 +2032,8 @@ cxx_omp_finish_clause (tree c, gimple_se
     return;
 
   decl = OMP_CLAUSE_DECL (c);
+  /* Correct for sizeless types: we're not changing the OpenMP
+     rules for those.  */
   decl = require_complete_type (decl);
   inner_type = TREE_TYPE (decl);
   if (decl == error_mark_node)
@@ -2271,7 +2273,7 @@ cp_fold (tree x)
 	  tree val = get_base_address (op0);
 	  if (val
 	      && INDIRECT_REF_P (val)
-	      && COMPLETE_TYPE_P (TREE_TYPE (val))
+	      && DEFINITE_TYPE_P (TREE_TYPE (val))
 	      && TREE_CONSTANT (TREE_OPERAND (val, 0)))
 	    {
 	      val = TREE_OPERAND (val, 0);
Index: gcc/cp/cvt.c
===================================================================
--- gcc/cp/cvt.c	2018-08-21 14:47:07.531170069 +0100
+++ gcc/cp/cvt.c	2018-10-15 14:13:37.848189957 +0100
@@ -85,7 +85,7 @@ cp_convert_to_pointer (tree type, tree e
   if (MAYBE_CLASS_TYPE_P (intype))
     {
       intype = complete_type (intype);
-      if (!COMPLETE_TYPE_P (intype))
+      if (!DEFINITE_TYPE_P (intype))
 	{
 	  if (complain & tf_error)
 	    error_at (loc, "can%'t convert from incomplete type %qH to %qI",
@@ -1169,10 +1169,10 @@ convert_to_void (tree expr, impl_conv_vo
 	tree type = TREE_TYPE (expr);
 	int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
 	int is_volatile = TYPE_VOLATILE (type);
-	int is_complete = COMPLETE_TYPE_P (complete_type (type));
+	int is_definite = DEFINITE_TYPE_P (complete_type (type));
 
 	/* Can't load the value if we don't know the type.  */
-	if (is_volatile && !is_complete)
+	if (is_volatile && !is_definite)
           {
             if (complain & tf_warning)
 	      switch (implicit)
@@ -1302,7 +1302,10 @@ convert_to_void (tree expr, impl_conv_vo
 		    gcc_unreachable ();
 		}
 	  }
-	if (is_reference || !is_volatile || !is_complete || TREE_ADDRESSABLE (type))
+	if (is_reference
+	    || !is_volatile
+	    || !is_definite
+	    || TREE_ADDRESSABLE (type))
           {
             /* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
                operation is stripped off. Note that we don't warn about
@@ -1328,9 +1331,9 @@ convert_to_void (tree expr, impl_conv_vo
       {
 	/* External variables might be incomplete.  */
 	tree type = TREE_TYPE (expr);
-	int is_complete = COMPLETE_TYPE_P (complete_type (type));
+	int is_definite = DEFINITE_TYPE_P (complete_type (type));
 
-	if (TYPE_VOLATILE (type) && !is_complete && (complain & tf_warning))
+	if (TYPE_VOLATILE (type) && !is_definite && (complain & tf_warning))
 	  switch (implicit)
 	    {
 	      case ICV_CAST:
@@ -1735,6 +1738,8 @@ build_expr_type_conversion (int desires,
 
   /* The code for conversions from class type is currently only used for
      delete expressions.  Other expressions are handled by build_new_op.  */
+  /* ...shouldn't be relevant for sizeless types in that case, since we
+     can't delete those.  */
   if (!complete_type_or_maybe_complain (basetype, expr, complain))
     return error_mark_node;
   if (!TYPE_HAS_CONVERSION (basetype))
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	2018-10-15 14:08:45.558612230 +0100
+++ gcc/cp/decl.c	2018-10-15 14:13:37.852189924 +0100
@@ -2876,8 +2876,8 @@ redeclaration_error_message (tree newdec
 
       if (TREE_CODE (DECL_TEMPLATE_RESULT (newdecl)) == TYPE_DECL)
 	{
-	  if (COMPLETE_TYPE_P (TREE_TYPE (newdecl))
-	      && COMPLETE_TYPE_P (TREE_TYPE (olddecl)))
+	  if (DEFINITE_TYPE_P (TREE_TYPE (newdecl))
+	      && DEFINITE_TYPE_P (TREE_TYPE (olddecl)))
 	    return G_("redefinition of %q#D");
 	  return NULL;
 	}
@@ -3851,7 +3851,7 @@ make_typename_type (tree context, tree n
     {
       if (complain & tf_error)
 	{
-	  if (!COMPLETE_TYPE_P (context))
+	  if (!DEFINITE_TYPE_P (context))
 	    cxx_incomplete_type_error (NULL_TREE, context);
 	  else
 	    error (want_template ? G_("no class template named %q#T in %q#T")
@@ -4740,6 +4740,9 @@ fixup_anonymous_aggr (tree t)
 	      }
 	  }
     }
+  if (TYPE_SIZELESS_P (t) && !TYPE_SIZELESS_P (DECL_CONTEXT (TYPE_NAME (t))))
+    error ("%q#T cannot have anonymous sizeless fields",
+	   DECL_CONTEXT (TYPE_NAME (t)));
 }
 
 /* Warn for an attribute located at LOCATION that appertains to the
@@ -5103,7 +5106,7 @@ start_decl (const cp_declarator *declara
     warning_at (DECL_SOURCE_LOCATION (decl), 0,
 		"inline function %qD given attribute noinline", decl);
 
-  if (TYPE_P (context) && COMPLETE_TYPE_P (complete_type (context)))
+  if (TYPE_P (context) && DEFINITE_TYPE_P (complete_type (context)))
     {
       bool this_tmpl = (processing_template_decl
 			> template_class_depth (context));
@@ -5254,7 +5257,7 @@ start_decl (const cp_declarator *declara
 start_decl_1 (tree decl, bool initialized)
 {
   tree type;
-  bool complete_p;
+  bool definite_p;
   bool aggregate_definition_p;
 
   gcc_assert (!processing_template_decl);
@@ -5265,31 +5268,44 @@ start_decl_1 (tree decl, bool initialize
   gcc_assert (VAR_P (decl));
 
   type = TREE_TYPE (decl);
-  complete_p = COMPLETE_TYPE_P (type);
+  definite_p = DEFINITE_TYPE_P (type);
   aggregate_definition_p = MAYBE_CLASS_TYPE_P (type) && !DECL_EXTERNAL (decl);
 
   /* If an explicit initializer is present, or if this is a definition
-     of an aggregate, then we need a complete type at this point.
-     (Scalars are always complete types, so there is nothing to
-     check.)  This code just sets COMPLETE_P; errors (if necessary)
+     of an aggregate, then we need a definite type at this point.
+     (Scalars are always definite types, so there is nothing to
+     check.)  This code just sets DEFINITE_P; errors (if necessary)
      are issued below.  */
   if ((initialized || aggregate_definition_p) 
-      && !complete_p
-      && COMPLETE_TYPE_P (complete_type (type)))
+      && !definite_p
+      && DEFINITE_TYPE_P (complete_type (type)))
     {
-      complete_p = true;
+      definite_p = true;
       /* We will not yet have set TREE_READONLY on DECL if the type
-	 was "const", but incomplete, before this point.  But, now, we
-	 have a complete type, so we can try again.  */
+	 was "const", but indefinite, before this point.  But, now, we
+	 have a definite type, so we can try again.  */
       cp_apply_type_quals_to_decl (cp_type_quals (type), decl);
     }
 
-  if (initialized)
+  if ((TREE_STATIC (decl) || DECL_EXTERNAL (decl))
+      && TYPE_SIZELESS_P (type))
+    {
+      if (DECL_THREAD_LOCAL_P (decl))
+	error_at (DECL_SOURCE_LOCATION (decl),
+		  "sizeless variable %q#D cannot have thread-local"
+		  " storage duration", decl);
+      else
+	error_at (DECL_SOURCE_LOCATION (decl),
+		  "sizeless variable %q#D cannot have static storage"
+		  " duration", decl);
+      type = TREE_TYPE (decl) = error_mark_node;
+    }
+  else if (initialized)
     /* Is it valid for this decl to have an initializer at all?  */
     {
-      /* Don't allow initializations for incomplete types except for
+      /* Don't allow initializations for indefinite types except for
 	 arrays which might be completed by the initialization.  */
-      if (complete_p)
+      if (definite_p)
 	;			/* A complete type is ok.  */
       else if (type_uses_auto (type))
 	; 			/* An auto type is ok.  */
@@ -5298,6 +5314,7 @@ start_decl_1 (tree decl, bool initialize
 	  error ("variable %q#D has initializer but incomplete type", decl);
 	  type = TREE_TYPE (decl) = error_mark_node;
 	}
+      /* Correct for sizeless types, which aren't allowed in arrays.  */
       else if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (type))))
 	{
 	  if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl))
@@ -5305,7 +5322,7 @@ start_decl_1 (tree decl, bool initialize
 	  /* else we already gave an error in start_decl.  */
 	}
     }
-  else if (aggregate_definition_p && !complete_p)
+  else if (aggregate_definition_p && !definite_p)
     {
       if (type_uses_auto (type))
 	gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (type));
@@ -5537,7 +5554,7 @@ layout_var_decl (tree decl)
     complete_type (type);
   if (!DECL_SIZE (decl)
       && TREE_TYPE (decl) != error_mark_node
-      && complete_or_array_type_p (type))
+      && definite_or_array_type_p (type))
     layout_decl (decl, 0);
 
   if (!DECL_EXTERNAL (decl) && DECL_SIZE (decl) == NULL_TREE)
@@ -6254,6 +6271,7 @@ check_array_initializer (tree decl, tree
   /* The array type itself need not be complete, because the
      initializer may tell us how many elements are in the array.
      But, the elements of the array must be complete.  */
+  /* Correct for sizeless types, which aren't allowed in arrays.  */
   if (!COMPLETE_TYPE_P (complete_type (element_type)))
     {
       if (decl)
@@ -6265,6 +6283,7 @@ check_array_initializer (tree decl, tree
     }
   /* A compound literal can't have variable size.  */
   if (init && !decl
+      /* Correct for sizeless types, which aren't used in arrays.  */
       && ((COMPLETE_TYPE_P (type) && !TREE_CONSTANT (TYPE_SIZE (type)))
 	  || !TREE_CONSTANT (TYPE_SIZE (element_type))))
     {
@@ -6320,7 +6339,7 @@ check_initializer (tree decl, tree init,
       if (check_array_initializer (decl, type, init))
 	return NULL_TREE;
     }
-  else if (!COMPLETE_TYPE_P (type))
+  else if (!DEFINITE_TYPE_P (type))
     {
       error_at (DECL_SOURCE_LOCATION (decl),
 		"%q#D has incomplete type", decl);
@@ -7093,7 +7112,7 @@ cp_finish_decl (tree decl, tree init, bo
 	 type, and that type has not been defined yet, delay emitting
 	 the debug information for it, as we will emit it later.  */
       if (TYPE_MAIN_DECL (TREE_TYPE (decl)) == decl
-	  && !COMPLETE_TYPE_P (TREE_TYPE (decl)))
+	  && !DEFINITE_TYPE_P (TREE_TYPE (decl)))
 	TYPE_DECL_SUPPRESS_DEBUG (decl) = 1;
 
       rest_of_decl_compilation (decl, DECL_FILE_SCOPE_P (decl),
@@ -7423,6 +7442,7 @@ get_tuple_size (tree type)
 				     /*context*/std_node,
 				     /*entering_scope*/false, tf_none);
   inst = complete_type (inst);
+  /* Correct for sizeless types in std::tuple_size is always a sized class.  */
   if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
     return NULL_TREE;
   tree val = lookup_qualified_name (inst, value_identifier,
@@ -7614,7 +7634,7 @@ cp_finish_decomp (tree decl, tree first,
       type = complete_type (TREE_TYPE (type));
       if (type == error_mark_node)
 	goto error_out;
-      if (!COMPLETE_TYPE_P (type))
+      if (!DEFINITE_TYPE_P (type))
 	{
 	  error_at (loc, "structured binding refers to incomplete type %qT",
 		    type);
@@ -7788,7 +7808,7 @@ cp_finish_decomp (tree decl, tree first,
       error_at (loc, "cannot decompose lambda closure type %qT", type);
       goto error_out;
     }
-  else if (processing_template_decl && !COMPLETE_TYPE_P (type))
+  else if (processing_template_decl && !DEFINITE_TYPE_P (type))
     pedwarn (loc, 0, "structured binding refers to incomplete class type %qT",
 	     type);
   else
@@ -9517,6 +9537,8 @@ check_static_variable_definition (tree d
     ;
   else if (cxx_dialect >= cxx11 && !INTEGRAL_OR_ENUMERATION_TYPE_P (type))
     {
+      /* Correct for sizeless types, which aren't allowed to have
+	 static storage duration.  */
       if (!COMPLETE_TYPE_P (type))
 	error_at (DECL_SOURCE_LOCATION (decl),
 		  "in-class initialization of static data member %q#D of "
@@ -11731,11 +11753,11 @@ grokdeclarator (const cp_declarator *dec
 		       "extra qualification %<%T::%> on member %qs",
 		       ctype, name);
 	}
-      else if (/* If the qualifying type is already complete, then we
+      else if (/* If the qualifying type is already definite, then we
 		  can skip the following checks.  */
-	       !COMPLETE_TYPE_P (ctype)
+	       !DEFINITE_TYPE_P (ctype)
 	       && (/* If the function is being defined, then
-		      qualifying type must certainly be complete.  */
+		      qualifying type must certainly be definite.  */
 		   funcdef_flag
 		   /* A friend declaration of "T::f" is OK, even if
 		      "T" is a template parameter.  But, if this
@@ -11743,14 +11765,14 @@ grokdeclarator (const cp_declarator *dec
 		      must be a class.  */
 		   || (!friendp && !CLASS_TYPE_P (ctype))
 		   /* For a declaration, the type need not be
-		      complete, if either it is dependent (since there
-		      is no meaningful definition of complete in that
+		      definite, if either it is dependent (since there
+		      is no meaningful definition of definite in that
 		      case) or the qualifying class is currently being
 		      defined.  */
 		   || !(dependent_type_p (ctype)
 			|| currently_open_class (ctype)))
-	       /* Check that the qualifying type is complete.  */
-	       && !complete_type_or_else (ctype, NULL_TREE))
+	       /* Check that the qualifying type is definite.  */
+	       && !definite_type_or_else (ctype, NULL_TREE))
 	return error_mark_node;
       else if (TREE_CODE (type) == FUNCTION_TYPE)
 	{
@@ -12265,6 +12287,13 @@ grokdeclarator (const cp_declarator *dec
 		    return error_mark_node;
 		  }
 
+		if (virtualp && TYPE_SIZELESS_P (ctype))
+		  {
+		    error_at (declspecs->locations[ds_virtual],
+			      "sizeless types cannot have virtual functions");
+		    return error_mark_node;
+		  }
+
 		if (virtualp
 		    && identifier_p (unqualified_id)
 		    && IDENTIFIER_NEWDEL_OP_P (unqualified_id))
@@ -12383,10 +12412,16 @@ grokdeclarator (const cp_declarator *dec
 	  }
 	else if (!staticp && !dependent_type_p (type)
 		 && !COMPLETE_TYPE_P (complete_type (type))
+		 /* Sizeless fields are OK in sizeless structs.  */
+		 && (!ctype
+		     || !TYPE_SIZELESS_P (ctype)
+		     || !DEFINITE_TYPE_P (type))
 		 && (!complete_or_array_type_p (type)
 		     || initialized == 0))
 	  {
 	    if (TREE_CODE (type) != ARRAY_TYPE
+		/* Correct for sizeless types, which aren't allowed in
+		   arrays.  */
 		|| !COMPLETE_TYPE_P (TREE_TYPE (type)))
 	      {
 		if (unqualified_id)
@@ -12785,7 +12820,7 @@ require_complete_types_for_parms (tree p
       if (dependent_type_p (TREE_TYPE (parms)))
 	continue;
       if (!VOID_TYPE_P (TREE_TYPE (parms))
-	  && complete_type_or_else (TREE_TYPE (parms), parms))
+	  && definite_type_or_else (TREE_TYPE (parms), parms))
 	{
 	  relayout_decl (parms);
 	  DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms));
@@ -13573,6 +13608,8 @@ grok_op_properties (tree decl, bool comp
 			class_type);
 	  /* Don't force t to be complete here.  */
 	  else if (MAYBE_CLASS_TYPE_P (t)
+		   /* Correct for sizeless type because they will never
+		      be bases or have bases.  */
 		   && COMPLETE_TYPE_P (t)
 		   && DERIVED_FROM_P (t, class_type))
 	    warning_at (loc, OPT_Wclass_conversion,
@@ -13653,6 +13690,8 @@ tag_name (enum tag_types code)
       return "union";
     case enum_type:
       return "enum";
+    case sizeless_struct_type:
+      return "struct";
     case typename_type:
       return "typename";
     default:
@@ -13880,6 +13919,7 @@ xref_tag_1 (enum tag_types tag_code, tre
     {
     case record_type:
     case class_type:
+    case sizeless_struct_type:
       code = RECORD_TYPE;
       break;
     case union_type:
@@ -13961,6 +14001,8 @@ xref_tag_1 (enum tag_types tag_code, tre
 	      /* And push it into current scope.  */
 	      scope = ts_current;
 	    }
+	  if (tag_code == sizeless_struct_type)
+	    TYPE_SIZELESS_P (t) = true;
 	  t = pushtag (name, t, scope);
 	}
     }
@@ -14083,6 +14125,7 @@ xref_basetypes (tree ref, tree base_list
 	  && CLASS_TYPE_P (basetype) && TYPE_BEING_DEFINED (basetype))
 	cxx_incomplete_type_diagnostic (NULL_TREE, basetype, DK_PEDWARN);
       if (!dependent_type_p (basetype)
+	  /* OK for sizeless types, which can never be bases.  */
 	  && !complete_type_or_else (basetype, NULL))
 	/* An incomplete type.  Remove it from the list.  */
 	*basep = TREE_CHAIN (*basep);
@@ -14126,6 +14169,11 @@ xref_basetypes (tree ref, tree base_list
 	  error ("derived union %qT invalid", ref);
 	  return;
 	}
+      if (TYPE_SIZELESS_P (ref))
+	{
+	  error ("sizeless type %qT cannot have base classes", ref);
+	  return;
+	}
     }
 
   if (max_bases > 1)
@@ -14662,7 +14710,7 @@ finish_enum_value_list (tree enumtype)
     TYPE_VALUES (t) = TYPE_VALUES (enumtype);
 
   if (at_class_scope_p ()
-      && COMPLETE_TYPE_P (current_class_type)
+      && DEFINITE_TYPE_P (current_class_type)
       && UNSCOPED_ENUM_P (enumtype))
     {
       insert_late_enum_def_bindings (current_class_type, enumtype);
@@ -14951,7 +14999,7 @@ check_function_type (tree decl, tree cur
   if (dependent_type_p (return_type)
       || type_uses_auto (return_type))
     return;
-  if (!COMPLETE_OR_VOID_TYPE_P (return_type))
+  if (!DEFINITE_OR_VOID_TYPE_P (return_type))
     {
       tree args = TYPE_ARG_TYPES (fntype);
 
@@ -15641,7 +15689,7 @@ begin_destructor_body (void)
      issued an error message.  We still want to try to process the
      body of the function, but initialize_vtbl_ptrs will crash if
      TYPE_BINFO is NULL.  */
-  if (COMPLETE_TYPE_P (current_class_type))
+  if (DEFINITE_TYPE_P (current_class_type))
     {
       compound_stmt = begin_compound_stmt (0);
       /* Make all virtual function table pointers in non-virtual base
@@ -16239,7 +16287,7 @@ maybe_register_incomplete_var (tree var)
 	inner_type = TREE_TYPE (inner_type);
       inner_type = TYPE_MAIN_VARIANT (inner_type);
 
-      if ((!COMPLETE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type))
+      if ((!DEFINITE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type))
 	  /* RTTI TD entries are created while defining the type_info.  */
 	  || (TYPE_LANG_SPECIFIC (inner_type)
 	      && TYPE_BEING_DEFINED (inner_type)))
Index: gcc/cp/decl2.c
===================================================================
--- gcc/cp/decl2.c	2018-08-21 14:47:07.527170104 +0100
+++ gcc/cp/decl2.c	2018-10-15 14:13:37.852189924 +0100
@@ -684,7 +684,7 @@ check_classfn (tree ctype, tree function
 
   if (!matched)
     {
-      if (!COMPLETE_TYPE_P (ctype))
+      if (!DEFINITE_TYPE_P (ctype))
 	cxx_incomplete_type_error (function, ctype);
       else
 	{
@@ -1403,6 +1403,8 @@ cp_check_const_attributes (tree attribut
 cp_omp_mappable_type (tree type)
 {
   /* Mappable type has to be complete.  */
+  /* Correct for sizeless types, since we're not changing the OpenMP
+     rules at this stage.  */
   if (type == error_mark_node || !COMPLETE_TYPE_P (type))
     return false;
   /* Arrays have mappable type if the elements have mappable type.  */
Index: gcc/cp/error.c
===================================================================
--- gcc/cp/error.c	2018-10-05 13:46:09.839798545 +0100
+++ gcc/cp/error.c	2018-10-15 14:13:37.852189924 +0100
@@ -660,6 +660,8 @@ class_key_or_enum_as_string (tree t)
     }
   else if (TREE_CODE (t) == UNION_TYPE)
     return "union";
+  else if (TYPE_SIZELESS_P (t))
+    return "__sizeless_struct";
   else if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
     return "class";
   else
@@ -4246,7 +4248,7 @@ qualified_name_lookup_error (tree scope,
     ; /* We already complained.  */
   else if (TYPE_P (scope))
     {
-      if (!COMPLETE_TYPE_P (scope))
+      if (!DEFINITE_TYPE_P (scope))
 	error_at (location, "incomplete type %qT used in nested name specifier",
 		  scope);
       else if (TREE_CODE (decl) == TREE_LIST)
Index: gcc/cp/except.c
===================================================================
--- gcc/cp/except.c	2018-09-25 08:03:44.163263685 +0100
+++ gcc/cp/except.c	2018-10-15 14:13:37.852189924 +0100
@@ -793,6 +793,7 @@ complete_ptr_ref_or_void_ptr_p (tree typ
   int is_ptr;
 
   /* Check complete.  */
+  /* Correct for sizeless types, which cannot be thrown.  */
   type = complete_type_or_else (type, from);
   if (!type)
     return 0;
@@ -805,6 +806,7 @@ complete_ptr_ref_or_void_ptr_p (tree typ
 
       if (is_ptr && VOID_TYPE_P (core))
 	/* OK */;
+      /* Correct for sizeless types, which cannot be thrown.  */
       else if (!complete_type_or_else (core, from))
 	return 0;
     }
Index: gcc/cp/expr.c
===================================================================
--- gcc/cp/expr.c	2018-06-27 10:27:09.702651350 +0100
+++ gcc/cp/expr.c	2018-10-15 14:13:37.852189924 +0100
@@ -40,7 +40,7 @@ cplus_expand_constant (tree cst)
 	member = PTRMEM_CST_MEMBER (cst);
 
 	/* We can't lower this until the class is complete.  */
-	if (!COMPLETE_TYPE_P (DECL_CONTEXT (member)))
+	if (!DEFINITE_TYPE_P (DECL_CONTEXT (member)))
 	  return cst;
 
 	if (TREE_CODE (member) == FIELD_DECL)
Index: gcc/cp/friend.c
===================================================================
--- gcc/cp/friend.c	2018-08-21 14:47:07.543169969 +0100
+++ gcc/cp/friend.c	2018-10-15 14:13:37.852189924 +0100
@@ -555,7 +555,7 @@ do_friend (tree ctype, tree declarator,
 	 to be a friend, so we do lookup here even if CTYPE is in
 	 the process of being defined.  */
       if (class_template_depth
-	  || COMPLETE_OR_OPEN_TYPE_P (ctype))
+	  || DEFINITE_OR_OPEN_TYPE_P (ctype))
 	{
 	  if (DECL_TEMPLATE_INFO (decl))
 	    /* DECL is a template specialization.  No need to
Index: gcc/cp/init.c
===================================================================
--- gcc/cp/init.c	2018-08-21 14:47:07.515170204 +0100
+++ gcc/cp/init.c	2018-10-15 14:13:37.852189924 +0100
@@ -388,7 +388,7 @@ build_value_init (tree type, tsubst_flag
 tree
 build_value_init_noctor (tree type, tsubst_flags_t complain)
 {
-  if (!COMPLETE_TYPE_P (type))
+  if (!DEFINITE_TYPE_P (type))
     {
       if (complain & tf_error)
 	error ("value-initialization of incomplete type %qT", type);
@@ -1279,7 +1279,7 @@ emit_mem_initializers (tree mem_inits)
 
   /* We will already have issued an error message about the fact that
      the type is incomplete.  */
-  if (!COMPLETE_TYPE_P (current_class_type))
+  if (!DEFINITE_TYPE_P (current_class_type))
     return;
 
   if (mem_inits
@@ -2150,7 +2150,7 @@ build_offset_ref (tree type, tree member
   gcc_assert (!DECL_P (member) || TREE_USED (member));
 
   type = TYPE_MAIN_VARIANT (type);
-  if (!COMPLETE_OR_OPEN_TYPE_P (complete_type (type)))
+  if (!DEFINITE_OR_OPEN_TYPE_P (complete_type (type)))
     {
       if (complain & tf_error)
 	error ("incomplete type %qT does not have member %qD", type, member);
@@ -2212,6 +2212,14 @@ build_offset_ref (tree type, tree member
     }
   else if (address_p && TREE_CODE (member) == FIELD_DECL)
     {
+      if (TYPE_SIZELESS_P (type))
+	{
+	  if (complain & tf_error)
+	    error ("cannot take address of member of sizeless type %qT",
+		   type);
+	  return error_mark_node;
+	}
+
       /* We need additional test besides the one in
 	 check_accessibility_of_qualified_id in case it is
 	 a pointer to non-static member.  */
@@ -3775,6 +3783,8 @@ build_new (vec<tree, va_gc> **placement,
   /* The type allocated must be complete.  If the new-type-id was
      "T[N]" then we are just checking that "T" is complete here, but
      that is equivalent, since the value of "N" doesn't matter.  */
+  /* Correct for sizeless types, which cannot be created via
+     operator new.  */
   if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
     return error_mark_node;
 
@@ -3833,7 +3843,15 @@ build_vec_delete_1 (tree base, tree maxi
   if (base == error_mark_node || maxindex == error_mark_node)
     return error_mark_node;
 
-  if (!COMPLETE_TYPE_P (type))
+  if (TYPE_SIZELESS_P (type))
+    {
+      if (complain & tf_error)
+	error ("cannot delete objects of sizeless type %qT", type);
+      /* This size won't actually be used.  */
+      size_exp = size_one_node;
+      goto no_destructor;
+    }
+  else if (!COMPLETE_TYPE_P (type))
     {
       if (complain & tf_warning)
 	{
@@ -4717,7 +4735,13 @@ build_delete (tree otype, tree addr, spe
       if (!VOID_TYPE_P (type))
 	{
 	  complete_type (type);
-	  if (!COMPLETE_TYPE_P (type))
+	  if (deleting && TYPE_SIZELESS_P (type))
+	    {
+	      if (complain & tf_error)
+		error ("cannot delete objects of sizeless type %qT", type);
+	      return error_mark_node;
+	    }
+	  else if (!DEFINITE_TYPE_P (type))
 	    {
 	      if (complain & tf_warning)
 		{
Index: gcc/cp/lambda.c
===================================================================
--- gcc/cp/lambda.c	2018-09-10 17:38:19.394814760 +0100
+++ gcc/cp/lambda.c	2018-10-15 14:13:37.852189924 +0100
@@ -198,7 +198,7 @@ lambda_function (tree lambda)
   gcc_assert (LAMBDA_TYPE_P (type));
   /* Don't let debug_tree cause instantiation.  */
   if (CLASSTYPE_TEMPLATE_INSTANTIATION (type)
-      && !COMPLETE_OR_OPEN_TYPE_P (type))
+      && !DEFINITE_OR_OPEN_TYPE_P (type))
     return NULL_TREE;
   lambda = lookup_member (type, call_op_identifier,
 			  /*protect=*/0, /*want_type=*/false,
@@ -590,6 +590,8 @@ add_capture (tree lambda, tree id, tree
 	{
 	  /* Capture by copy requires a complete type.  */
 	  type = complete_type (type);
+	  /* Correct for sizeless types, which need to be captured by
+	     reference instead.  */
 	  if (!COMPLETE_TYPE_P (type))
 	    {
 	      error ("capture by copy of incomplete type %qT", type);
@@ -644,7 +646,7 @@ add_capture (tree lambda, tree id, tree
   if (current_class_type
       && current_class_type == LAMBDA_EXPR_CLOSURE (lambda))
     {
-      if (COMPLETE_TYPE_P (current_class_type))
+      if (DEFINITE_TYPE_P (current_class_type))
 	internal_error ("trying to capture %qD in instantiation of "
 			"generic lambda", id);
       finish_member_declaration (member);
@@ -783,7 +785,7 @@ lambda_expr_this_capture (tree lambda, b
 	      /* Lambda in an NSDMI.  We don't have a function to look up
 		 'this' in, but we can find (or rebuild) the fake one from
 		 inject_this_parameter.  */
-	      if (!containing_function && !COMPLETE_TYPE_P (closure))
+	      if (!containing_function && !DEFINITE_TYPE_P (closure))
 		/* If we're parsing a lambda in a non-local class,
 		   we can find the fake 'this' in scope_chain.  */
 		init = scope_chain->x_current_class_ptr;
Index: gcc/cp/name-lookup.c
===================================================================
--- gcc/cp/name-lookup.c	2018-10-05 13:46:09.843798513 +0100
+++ gcc/cp/name-lookup.c	2018-10-15 14:13:37.856189890 +0100
@@ -1200,7 +1200,7 @@ fields_linear_search (tree klass, tree n
 tree
 search_anon_aggr (tree anon, tree name, bool want_type)
 {
-  gcc_assert (COMPLETE_TYPE_P (anon));
+  gcc_assert (DEFINITE_TYPE_P (anon));
   tree ret = get_class_binding_direct (anon, name, want_type);
   return ret;
 }
@@ -1224,7 +1224,7 @@ get_class_binding_direct (tree klass, tr
   tree val = NULL_TREE;
   vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass);
 
-  if (COMPLETE_TYPE_P (klass) && member_vec)
+  if (DEFINITE_TYPE_P (klass) && member_vec)
     {
       val = member_vec_binary_search (member_vec, lookup);
       if (!val)
@@ -1288,7 +1288,7 @@ get_class_binding (tree klass, tree name
 {
   klass = complete_type (klass);
 
-  if (COMPLETE_TYPE_P (klass))
+  if (DEFINITE_TYPE_P (klass))
     {
       /* Lazily declare functions, if we're going to search these.  */
       if (IDENTIFIER_CTOR_P (name))
@@ -1325,16 +1325,16 @@ get_class_binding (tree klass, tree name
 tree *
 find_member_slot (tree klass, tree name)
 {
-  bool complete_p = COMPLETE_TYPE_P (klass);
+  bool definite_p = DEFINITE_TYPE_P (klass);
 
   vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass);
   if (!member_vec)
     {
       vec_alloc (member_vec, 8);
       CLASSTYPE_MEMBER_VEC (klass) = member_vec;
-      if (complete_p)
+      if (definite_p)
 	{
-	  /* If the class is complete but had no member_vec, we need
+	  /* If the class is definite but had no member_vec, we need
 	     to add the TYPE_FIELDS into it.  We're also most likely
 	     to be adding ctors & dtors, so ask for 6 spare slots (the
 	     abstract cdtors and their clones).  */
@@ -1369,12 +1369,12 @@ find_member_slot (tree klass, tree name)
 	  return slot;
 	}
 
-      if (complete_p && fn_name > name)
+      if (definite_p && fn_name > name)
 	break;
     }
 
   /* No slot found, add one if the class is complete.  */
-  if (complete_p)
+  if (definite_p)
     {
       /* Do exact allocation, as we don't expect to add many.  */
       gcc_assert (name != conv_op_identifier);
@@ -1393,7 +1393,7 @@ find_member_slot (tree klass, tree name)
 tree *
 add_member_slot (tree klass, tree name)
 {
-  gcc_assert (!COMPLETE_TYPE_P (klass));
+  gcc_assert (!DEFINITE_TYPE_P (klass));
 
   vec<tree, va_gc> *member_vec = CLASSTYPE_MEMBER_VEC (klass);
   vec_safe_push (member_vec, NULL_TREE);
@@ -6484,7 +6484,7 @@ maybe_process_template_type_declaration
 	    {
 	      finish_member_declaration (CLASSTYPE_TI_TEMPLATE (type));
 
-	      if (!COMPLETE_TYPE_P (current_class_type))
+	      if (!DEFINITE_TYPE_P (current_class_type))
 		{
 		  maybe_add_class_template_decl_list (current_class_type,
 						      type, /*friend_p=*/0);
@@ -6665,7 +6665,7 @@ do_pushtag (tree name, tree type, tag_sc
     }
 
   if (b->kind == sk_class
-      && !COMPLETE_TYPE_P (current_class_type))
+      && !DEFINITE_TYPE_P (current_class_type))
     {
       maybe_add_class_template_decl_list (current_class_type,
 					  type, /*friend_p=*/0);
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	2018-10-15 14:08:45.566612164 +0100
+++ gcc/cp/parser.c	2018-10-15 14:13:37.860189857 +0100
@@ -940,6 +940,7 @@ cp_keyword_starts_decl_specifier_p (enum
     case RID_ENUM:
     case RID_CLASS:
     case RID_STRUCT:
+    case RID_SIZELESS_STRUCT:
     case RID_UNION:
     case RID_TYPENAME:
       /* Simple type specifiers.  */
@@ -3415,7 +3416,7 @@ cp_parser_diagnose_invalid_type_name (cp
       else if (TYPE_P (parser->scope))
 	{
 	  auto_diagnostic_group d;
-	  if (!COMPLETE_TYPE_P (parser->scope))
+	  if (!DEFINITE_TYPE_P (parser->scope))
 	    cxx_incomplete_type_error (location_of (id), NULL_TREE,
 				       parser->scope);
 	  else if (cp_lexer_next_token_is (parser->lexer, CPP_LESS))
@@ -6468,7 +6469,7 @@ cp_parser_nested_name_specifier_opt (cp_
       if (TYPE_P (new_scope)
 	  /* Since checking types for dependency can be expensive,
 	     avoid doing it if the type is already complete.  */
-	  && !COMPLETE_TYPE_P (new_scope)
+	  && !DEFINITE_TYPE_P (new_scope)
 	  /* Do not try to complete dependent types.  */
 	  && !dependent_type_p (new_scope))
 	{
@@ -6476,7 +6477,7 @@ cp_parser_nested_name_specifier_opt (cp_
 	  /* If it is a typedef to current class, use the current
 	     class instead, as the typedef won't have any names inside
 	     it yet.  */
-	  if (!COMPLETE_TYPE_P (new_scope)
+	  if (!DEFINITE_TYPE_P (new_scope)
 	      && currently_open_class (new_scope))
 	    new_scope = TYPE_MAIN_VARIANT (new_scope);
 	}
@@ -7522,7 +7523,7 @@ cp_parser_postfix_dot_deref_expression (
 	  && !currently_open_class (scope))
 	{
 	  scope = complete_type (scope);
-	  if (!COMPLETE_TYPE_P (scope)
+	  if (!DEFINITE_TYPE_P (scope)
 	      && cp_parser_dot_deref_incomplete (&scope, &postfix_expression,
 						 &dependent_p))
 	    return error_mark_node;
@@ -12134,7 +12135,7 @@ cp_parser_perform_range_for_lookup (tree
       return error_mark_node;
     }
 
-  if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (range))))
+  if (!DEFINITE_TYPE_P (complete_type (TREE_TYPE (range))))
     {
       error ("range-based %<for%> expression of type %qT "
 	     "has incomplete type", TREE_TYPE (range));
@@ -16899,6 +16900,7 @@ cp_parser_type_specifier (cp_parser* par
 	 elaborated-type-specifier.  */
     case RID_CLASS:
     case RID_STRUCT:
+    case RID_SIZELESS_STRUCT:
     case RID_UNION:
       if ((flags & CP_PARSER_FLAGS_NO_TYPE_DEFINITIONS))
 	goto elaborated_type_specifier;
@@ -23343,7 +23345,7 @@ cp_parser_class_head (cp_parser* parser,
 
   /* If this type was already complete, and we see another definition,
      that's an error.  */
-  if (type != error_mark_node && COMPLETE_TYPE_P (type))
+  if (type != error_mark_node && DEFINITE_TYPE_P (type))
     {
       error_at (type_start_token->location, "redefinition of %q#T",
 		type);
@@ -28844,6 +28846,8 @@ cp_parser_token_is_class_key (cp_token*
       return record_type;
     case RID_UNION:
       return union_type;
+    case RID_SIZELESS_STRUCT:
+      return sizeless_struct_type;
 
     default:
       return none_type;
@@ -28878,11 +28882,15 @@ cp_parser_check_class_key (enum tag_type
 {
   if (type == error_mark_node)
     return;
-  if ((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type))
+  if (((TREE_CODE (type) == UNION_TYPE) != (class_key == union_type))
+      || ((TREE_CODE (type) == RECORD_TYPE && TYPE_SIZELESS_P (type))
+	  != (class_key == sizeless_struct_type)))
     {
       if (permerror (input_location, "%qs tag used in naming %q#T",
 		     class_key == union_type ? "union"
-		     : class_key == record_type ? "struct" : "class",
+		     : class_key == record_type ? "struct"
+		     : class_key == sizeless_struct_type ? "__sizeless_struct"
+		     : "class",
 		     type))
 	inform (DECL_SOURCE_LOCATION (TYPE_NAME (type)),
 		"%q#T was previously declared here", type);
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	2018-10-15 14:08:45.566612164 +0100
+++ gcc/cp/pt.c	2018-10-15 14:13:37.860189857 +0100
@@ -843,7 +843,7 @@ maybe_new_partial_specialization (tree t
   //
   // Here, S<T*> is an implicit instantiation of S whose type
   // is incomplete.
-  if (CLASSTYPE_IMPLICIT_INSTANTIATION (type) && !COMPLETE_TYPE_P (type))
+  if (CLASSTYPE_IMPLICIT_INSTANTIATION (type) && !DEFINITE_TYPE_P (type))
     return type;
 
   // It can also be the case that TYPE is a completed specialization.
@@ -1020,7 +1020,7 @@ maybe_process_partial_specialization (tr
 	 and `C<T>::D' may not exist.  */
 
       if (CLASSTYPE_IMPLICIT_INSTANTIATION (context)
-	  && !COMPLETE_TYPE_P (type))
+	  && !DEFINITE_TYPE_P (type))
 	{
 	  tree t;
 	  tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
@@ -1044,7 +1044,7 @@ maybe_process_partial_specialization (tr
 	    {
 	      tree inst = TREE_VALUE (t);
 	      if (CLASSTYPE_TEMPLATE_SPECIALIZATION (inst)
-		  || !COMPLETE_OR_OPEN_TYPE_P (inst))
+		  || !DEFINITE_OR_OPEN_TYPE_P (inst))
 		{
 		  /* We already have a full specialization of this partial
 		     instantiation, or a full specialization has been
@@ -5029,7 +5029,7 @@ process_partial_specialization (tree dec
 
   /* We should only get here once.  */
   if (TREE_CODE (decl) == TYPE_DECL)
-    gcc_assert (!COMPLETE_TYPE_P (type));
+    gcc_assert (!DEFINITE_TYPE_P (type));
 
   // Build the template decl.
   tree tmpl = build_template_decl (decl, current_template_parms,
@@ -5066,7 +5066,7 @@ process_partial_specialization (tree dec
     {
       tree instance = TREE_VALUE (inst);
       if (TYPE_P (instance)
-	  ? (COMPLETE_TYPE_P (instance)
+	  ? (DEFINITE_TYPE_P (instance)
 	     && CLASSTYPE_IMPLICIT_INSTANTIATION (instance))
 	  : DECL_TEMPLATE_INSTANTIATION (instance))
 	{
@@ -9464,6 +9464,7 @@ lookup_template_class_1 (tree d1, tree a
 	  t = make_class_type (TREE_CODE (template_type));
 	  CLASSTYPE_DECLARED_CLASS (t)
 	    = CLASSTYPE_DECLARED_CLASS (template_type);
+	  TYPE_SIZELESS_P (t) = TYPE_SIZELESS_P (template_type);
 	  SET_CLASSTYPE_IMPLICIT_INSTANTIATION (t);
 
 	  /* A local class.  Make sure the decl gets registered properly.  */
@@ -10508,10 +10509,10 @@ tsubst_friend_function (tree decl, tree
       --processing_template_decl;
 
       if (!dependent_p
-	  && !complete_type_or_else (context, NULL_TREE))
+	  && !definite_type_or_else (context, NULL_TREE))
 	return error_mark_node;
 
-      if (COMPLETE_TYPE_P (context))
+      if (DEFINITE_TYPE_P (context))
 	{
 	  tree fn = new_friend;
 	  /* do_friend adds the TEMPLATE_DECL for any member friend
@@ -10625,7 +10626,7 @@ can_complete_type_without_circularity (t
 {
   if (type == NULL_TREE || type == error_mark_node)
     return 0;
-  else if (COMPLETE_TYPE_P (type))
+  else if (DEFINITE_TYPE_P (type))
     return 1;
   else if (TREE_CODE (type) == ARRAY_TYPE)
     return can_complete_type_without_circularity (TREE_TYPE (type));
@@ -10868,7 +10869,7 @@ instantiate_class_template_1 (tree type)
   if (type == error_mark_node)
     return error_mark_node;
 
-  if (COMPLETE_OR_OPEN_TYPE_P (type)
+  if (DEFINITE_OR_OPEN_TYPE_P (type)
       || uses_template_parms (type))
     return type;
 
@@ -10909,9 +10910,9 @@ instantiate_class_template_1 (tree type)
       args = CLASSTYPE_TI_ARGS (type);
     }
 
-  /* If the template we're instantiating is incomplete, then clearly
+  /* If the template we're instantiating is indefinite, then clearly
      there's nothing we can do.  */
-  if (!COMPLETE_TYPE_P (pattern))
+  if (!DEFINITE_TYPE_P (pattern))
     {
       /* We can try again later.  */
       TYPE_BEING_DEFINED (type) = 0;
@@ -10971,7 +10972,7 @@ instantiate_class_template_1 (tree type)
      instantiate it, and that lookup should instantiate the enclosing
      class.  */
   gcc_assert (!DECL_CLASS_SCOPE_P (TYPE_MAIN_DECL (pattern))
-	      || COMPLETE_OR_OPEN_TYPE_P (TYPE_CONTEXT (type)));
+	      || DEFINITE_OR_OPEN_TYPE_P (TYPE_CONTEXT (type)));
 
   base_list = NULL_TREE;
   if (BINFO_N_BASE_BINFOS (pbinfo))
@@ -11212,7 +11213,9 @@ instantiate_class_template_1 (tree type)
 			  if (can_complete_type_without_circularity (rtype))
 			    complete_type (rtype);
 
-			  if (!complete_or_array_type_p (rtype))
+			  if (TYPE_SIZELESS_P (type)
+			      ? !definite_or_array_type_p (rtype)
+			      : !complete_or_array_type_p (rtype))
 			    {
 			      /* If R's type couldn't be completed and
 				 it isn't a flexible array member (whose
@@ -14857,7 +14860,7 @@ tsubst (tree t, tree args, tsubst_flags_
 	       point, so here CTX really should have complete type, unless
 	       it's a partial instantiation.  */
 	    ctx = complete_type (ctx);
-	    if (!COMPLETE_TYPE_P (ctx))
+	    if (!DEFINITE_TYPE_P (ctx))
 	      {
 		if (complain & tf_error)
 		  cxx_incomplete_type_error (NULL_TREE, ctx);
@@ -23299,7 +23302,7 @@ do_type_instantiation (tree t, tree stor
 
   complete_type (t);
 
-  if (!COMPLETE_TYPE_P (t))
+  if (!DEFINITE_TYPE_P (t))
     {
       if (complain & tf_error)
 	error ("explicit instantiation of %q#T before definition of template",
@@ -24194,7 +24197,7 @@ instantiate_pending_templates (int retri
 
 	  if (TYPE_P (instantiation))
 	    {
-	      if (!COMPLETE_TYPE_P (instantiation))
+	      if (!DEFINITE_TYPE_P (instantiation))
 		{
 		  instantiate_class_template (instantiation);
 		  if (CLASSTYPE_TEMPLATE_INSTANTIATION (instantiation))
@@ -24208,11 +24211,11 @@ instantiate_pending_templates (int retri
 					  /*defer_ok=*/false,
 					  /*expl_inst_class_mem_p=*/false);
 
-		  if (COMPLETE_TYPE_P (instantiation))
+		  if (DEFINITE_TYPE_P (instantiation))
 		    reconsider = 1;
 		}
 
-	      complete = COMPLETE_TYPE_P (instantiation);
+	      complete = DEFINITE_TYPE_P (instantiation);
 	    }
 	  else
 	    {
Index: gcc/cp/rtti.c
===================================================================
--- gcc/cp/rtti.c	2018-10-15 14:08:45.562612196 +0100
+++ gcc/cp/rtti.c	2018-10-15 14:13:37.860189857 +0100
@@ -281,6 +281,7 @@ get_tinfo_decl_dynamic (tree exp, tsubst
   /* For UNKNOWN_TYPEs call complete_type_or_else to get diagnostics.  */
   if (CLASS_TYPE_P (type) || type == unknown_type_node
       || type == init_list_type_node)
+    /* Correct for sizeless types, which have no typeid.  */
     type = complete_type_or_maybe_complain (type, exp, complain);
 
   if (!type)
@@ -314,6 +315,7 @@ typeid_ok_p (void)
       return false;
     }
 
+  /* Correct for sizeless types, for which typeid is not allowed.  */
   if (!COMPLETE_TYPE_P (const_type_info_type_node))
     {
       gcc_rich_location richloc (input_location);
@@ -523,6 +525,7 @@ get_typeid (tree type, tsubst_flags_t co
   /* For UNKNOWN_TYPEs call complete_type_or_else to get diagnostics.  */
   if (CLASS_TYPE_P (type) || type == unknown_type_node
       || type == init_list_type_node)
+    /* Correct for sizeless types, which have no typeid.  */
     type = complete_type_or_maybe_complain (type, NULL_TREE, complain);
 
   if (!type)
@@ -575,6 +578,7 @@ build_dynamic_cast_1 (tree type, tree ex
 	  errstr = _("target is not pointer or reference to class");
 	  goto fail;
 	}
+      /* Correct for sizeless types, which can't be used with dynamic_cast.  */
       if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (type))))
 	{
 	  errstr = _("target is not pointer or reference to complete type");
@@ -607,6 +611,7 @@ build_dynamic_cast_1 (tree type, tree ex
 	  errstr = _("source is not a pointer to class");
 	  goto fail;
 	}
+      /* Correct for sizeless types, which can't be used with dynamic_cast.  */
       if (!COMPLETE_TYPE_P (complete_type (TREE_TYPE (exprtype))))
 	{
 	  errstr = _("source is a pointer to incomplete type");
@@ -625,6 +630,7 @@ build_dynamic_cast_1 (tree type, tree ex
 	  errstr = _("source is not of class type");
 	  goto fail;
 	}
+      /* Correct for sizeless types, which can't be used with dynamic_cast.  */
       if (!COMPLETE_TYPE_P (complete_type (exprtype)))
 	{
 	  errstr = _("source is of incomplete class type");
@@ -944,6 +950,8 @@ tinfo_base_init (tinfo_s *ti, tree targe
 			    /*tag_scope=*/ts_current, false);
       pop_abi_namespace ();
 
+      /* Correct for sizeless types, which can't have base classes or
+	 virtual functions.  */
       if (!COMPLETE_TYPE_P (real_type))
 	{
 	  /* We never saw a definition of this type, so we need to
Index: gcc/cp/search.c
===================================================================
--- gcc/cp/search.c	2018-08-21 14:47:07.543169969 +0100
+++ gcc/cp/search.c	2018-10-15 14:13:37.864189824 +0100
@@ -202,7 +202,7 @@ lookup_base (tree t, tree base, base_acc
 
   /* If BASE is incomplete, it can't be a base of T--and instantiating it
      might cause an error.  */
-  if (t_binfo && CLASS_TYPE_P (base) && COMPLETE_OR_OPEN_TYPE_P (base))
+  if (t_binfo && CLASS_TYPE_P (base) && DEFINITE_OR_OPEN_TYPE_P (base))
     {
       struct lookup_base_data_s data;
 
@@ -252,6 +252,8 @@ lookup_base (tree t, tree base, base_acc
 	       there's no need to issue another error here, and
 	       there's no implicit typedef to use in the code that
 	       follows, so we skip the check.  */
+	    /* Correct for sizeless types, which don't have base classes
+	       and can't be used as base classes.  */
 	    && COMPLETE_TYPE_P (base)
 	    && !accessible_base_p (t, base, !(access & ba_ignore_scope)))
 	  {
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	2018-10-05 13:46:09.839798545 +0100
+++ gcc/cp/semantics.c	2018-10-15 14:13:37.864189824 +0100
@@ -3887,6 +3887,7 @@ finish_underlying_type (tree type)
       return underlying_type;
     }
 
+  /* Correct for sizeless types; only relevant for enums.  */
   if (!complete_type_or_else (type, NULL_TREE))
     return error_mark_node;
 
@@ -3915,6 +3916,7 @@ finish_underlying_type (tree type)
 tree
 calculate_direct_bases (tree type, tsubst_flags_t complain)
 {
+  /* Part of a parameter pack, so probably correct for sizeless types.  */
   if (!complete_type_or_maybe_complain (type, NULL_TREE, complain)
       || !NON_UNION_CLASS_TYPE_P (type))
     return make_tree_vec (0);
@@ -3981,6 +3983,7 @@ calculate_bases_helper (tree type)
 tree
 calculate_bases (tree type, tsubst_flags_t complain)
 {
+  /* Part of a parameter pack, so probably correct for sizeless types.  */
   if (!complete_type_or_maybe_complain (type, NULL_TREE, complain)
       || !NON_UNION_CLASS_TYPE_P (type))
     return make_tree_vec (0);
@@ -4088,6 +4091,7 @@ finish_offsetof (tree object_ptr, tree e
     }
   if (REFERENCE_REF_P (expr))
     expr = TREE_OPERAND (expr, 0);
+  /* Cannot use offsetof for sizeless types.  */
   if (!complete_type_or_else (TREE_TYPE (TREE_TYPE (object_ptr)), object_ptr))
     return error_mark_node;
   if (warn_invalid_offsetof
@@ -7321,10 +7325,14 @@ finish_omp_clauses (tree clauses, enum c
 
       if (need_complete_type || need_copy_assignment)
 	{
+	  /* Correct for sizeless types: we're not changing the OpenMP
+	     rules for those.  */
 	  t = require_complete_type (t);
 	  if (t == error_mark_node)
 	    remove = true;
 	  else if (TYPE_REF_P (TREE_TYPE (t))
+		   /* OK for sizeless types: keeping OpenMP rules unchanged
+		      for now.  */
 		   && !complete_type_or_else (TREE_TYPE (TREE_TYPE (t)), t))
 	    remove = true;
 	}
@@ -7374,6 +7382,8 @@ finish_omp_clauses (tree clauses, enum c
 	 Save the results, because later we won't be in the right context
 	 for making these queries.  */
       if (CLASS_TYPE_P (inner_type)
+	  /* Correct for sizeless types: we're not changing the OpenMP
+	     rules for those.  */
 	  && COMPLETE_TYPE_P (inner_type)
 	  && (need_default_ctor || need_copy_ctor
 	      || need_copy_assignment || need_dtor)
@@ -7555,6 +7565,8 @@ finish_omp_threadprivate (tree vars)
 	error ("%qE declared %<threadprivate%> after first use", v);
       else if (! TREE_STATIC (v) && ! DECL_EXTERNAL (v))
 	error ("automatic variable %qE cannot be %<threadprivate%>", v);
+      /* Correct for sizeless types: we're not changing the OpenMP
+	 rules for those.  */
       else if (! COMPLETE_TYPE_P (complete_type (TREE_TYPE (v))))
 	error ("%<threadprivate%> %qE has incomplete type", v);
       else if (TREE_STATIC (v) && TYPE_P (CP_DECL_CONTEXT (v))
@@ -9105,13 +9117,14 @@ check_trait_type (tree type)
 	    && check_trait_type (TREE_CHAIN (type)));
 
   if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type)
+      /* Correct for sizeless types, which aren't allowed in arrays.  */
       && COMPLETE_TYPE_P (TREE_TYPE (type)))
     return true;
 
   if (VOID_TYPE_P (type))
     return true;
 
-  return !!complete_type_or_else (strip_array_types (type), NULL_TREE);
+  return !!definite_type_or_else (strip_array_types (type), NULL_TREE);
 }
 
 /* Process a trait expression.  */
@@ -9172,7 +9185,7 @@ finish_trait_expr (cp_trait_kind kind, t
     case CPTK_IS_BASE_OF:
       if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
 	  && !same_type_ignoring_top_level_qualifiers_p (type1, type2)
-	  && !complete_type_or_else (type2, NULL_TREE))
+	  && !definite_type_or_else (type2, NULL_TREE))
 	/* We already issued an error.  */
 	return error_mark_node;
       break;
@@ -9245,7 +9258,7 @@ apply_deduced_return_type (tree fco, tre
     return;
 
   if (!processing_template_decl && !VOID_TYPE_P (return_type)
-      && !complete_type_or_else (return_type, NULL_TREE))
+      && !definite_type_or_else (return_type, NULL_TREE))
     return;
 
   /* We already have a DECL_RESULT from start_preparsed_function.
Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	2018-10-05 13:46:09.839798545 +0100
+++ gcc/cp/tree.c	2018-10-15 14:13:37.864189824 +0100
@@ -646,7 +646,7 @@ build_cplus_new (tree type, tree init, t
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
-  if (!complete_type_or_maybe_complain (type, init, complain))
+  if (!definite_type_or_maybe_complain (type, init, complain))
     return error_mark_node;
 
   /* Make sure that we're not trying to create an instance of an
@@ -1061,6 +1061,7 @@ build_cplus_array_type (tree elt_type, t
 		     = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (elt_type));
 
   if (!dependent && t == TYPE_MAIN_VARIANT (t)
+      /* Correct for sizeless types, which aren't allowed in arrays.  */
       && !COMPLETE_TYPE_P (t) && COMPLETE_TYPE_P (elt_type))
     {
       /* The element type has been completed since the last time we saw
@@ -1648,7 +1649,7 @@ strip_typedefs (tree t, bool *remove_att
     }
   gcc_assert (!typedef_variant_p (result));
 
-  if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
+  if (DEFINITE_TYPE_P (result) && !DEFINITE_TYPE_P (t))
   /* If RESULT is complete and T isn't, it's likely the case that T
      is a variant of RESULT which hasn't been updated yet.  Skip the
      attribute handling.  */;
@@ -4063,7 +4064,7 @@ type_has_nontrivial_copy_init (const_tre
 
   if (CLASS_TYPE_P (t))
     {
-      gcc_assert (COMPLETE_TYPE_P (t));
+      gcc_assert (DEFINITE_TYPE_P (t));
 
       if (TYPE_HAS_COMPLEX_COPY_CTOR (t)
 	  || TYPE_HAS_COMPLEX_MOVE_CTOR (t))
@@ -4350,22 +4351,22 @@ record_has_unique_obj_representations (c
     else if (!type_has_unique_obj_representations (TREE_TYPE (field)))
       return false;
 
-  offset_int cur = 0;
+  poly_offset_int cur = 0;
   for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
     if (TREE_CODE (field) == FIELD_DECL)
       {
-	offset_int fld = wi::to_offset (DECL_FIELD_OFFSET (field));
+	poly_offset_int fld = wi::to_poly_offset (DECL_FIELD_OFFSET (field));
 	offset_int bitpos = wi::to_offset (DECL_FIELD_BIT_OFFSET (field));
 	fld = fld * BITS_PER_UNIT + bitpos;
-	if (cur != fld)
+	if (maybe_ne (cur, fld))
 	  return false;
 	if (DECL_SIZE (field))
 	  {
-	    offset_int size = wi::to_offset (DECL_SIZE (field));
+	    poly_offset_int size = wi::to_poly_offset (DECL_SIZE (field));
 	    cur += size;
 	  }
       }
-  if (cur != wi::to_offset (sz))
+  if (maybe_ne (cur, wi::to_poly_offset (sz)))
     return false;
 
   return true;
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c	2018-10-15 14:08:45.558612230 +0100
+++ gcc/cp/typeck.c	2018-10-15 14:13:37.864189824 +0100
@@ -66,13 +66,18 @@ static int convert_arguments (tree, vec<
 static bool is_std_move_p (tree);
 static bool is_std_forward_p (tree);
 
-/* Do `exp = require_complete_type (exp);' to make sure exp
-   does not have an incomplete type.  (That includes void types.)
-   Returns error_mark_node if the VALUE does not have
-   complete type when this function returns.  */
+/* Do:
+
+     value = require_complete_type_sfinae (value, complain, allow_sizeless_p);
+
+   to make sure VALUE does not have an incomplete type or indefinite type;
+   ALLOW_SIZELESS_P selects the latter over the former.  (Void types are
+   both incomplete and indefinite.)  Return error_mark_node if VALUE
+   does not meet the condition.  */
 
 tree
-require_complete_type_sfinae (tree value, tsubst_flags_t complain)
+require_complete_type_sfinae (tree value, tsubst_flags_t complain,
+			      bool allow_sizeless_p)
 {
   tree type;
 
@@ -88,10 +93,11 @@ require_complete_type_sfinae (tree value
     return error_mark_node;
 
   /* First, detect a valid value with a complete type.  */
-  if (COMPLETE_TYPE_P (type))
+  if (allow_sizeless_p ? DEFINITE_TYPE_P (type) : COMPLETE_TYPE_P (type))
     return value;
 
-  if (complete_type_or_maybe_complain (type, value, complain))
+  if (complete_type_or_maybe_complain (type, value, complain,
+				       allow_sizeless_p))
     return value;
   else
     return error_mark_node;
@@ -116,12 +122,13 @@ complete_type (tree type)
        at some point.  */
     return error_mark_node;
 
-  if (type == error_mark_node || COMPLETE_TYPE_P (type))
+  if (type == error_mark_node || DEFINITE_TYPE_P (type))
     ;
   else if (TREE_CODE (type) == ARRAY_TYPE)
     {
       tree t = complete_type (TREE_TYPE (type));
       unsigned int needs_constructing, has_nontrivial_dtor;
+      /* Correct for sizeless types, which aren't allowed in arrays.  */
       if (COMPLETE_TYPE_P (t) && !dependent_type_p (type))
 	layout_type (type);
       needs_constructing
@@ -140,18 +147,23 @@ complete_type (tree type)
   return type;
 }
 
-/* Like complete_type, but issue an error if the TYPE cannot be completed.
-   VALUE is used for informative diagnostics.
-   Returns NULL_TREE if the type cannot be made complete.  */
+/* Like complete_type, but check whether the result is definite or complete;
+   ALLOW_SIZELESS_P selects the former over the latter.  Return NULL_TREE if
+   the type doesn't meet this condition, and also report an error if
+   COMPLAIN allows.  VALUE is used for informative diagnostics.  */
 
 tree
-complete_type_or_maybe_complain (tree type, tree value, tsubst_flags_t complain)
+complete_type_or_maybe_complain (tree type, tree value,
+				 tsubst_flags_t complain,
+				 bool allow_sizeless_p)
 {
   type = complete_type (type);
   if (type == error_mark_node)
     /* We already issued an error.  */
     return NULL_TREE;
-  else if (!COMPLETE_TYPE_P (type))
+  else if (allow_sizeless_p
+	   ? !DEFINITE_TYPE_P (type)
+	   : !COMPLETE_TYPE_P (type))
     {
       if (complain & tf_error)
 	cxx_incomplete_type_diagnostic (value, type, DK_ERROR);
@@ -161,10 +173,15 @@ complete_type_or_maybe_complain (tree ty
     return type;
 }
 
+/* Like complete_type, but check whether the result is definite or complete;
+   ALLOW_SIZELESS_P selects the former over the latter.  Report an error
+   and return NULL_TREE if the type doesn't meet this condition.  */
+
 tree
-complete_type_or_else (tree type, tree value)
+complete_type_or_else (tree type, tree value, bool allow_sizeless_p)
 {
-  return complete_type_or_maybe_complain (type, value, tf_warning_or_error);
+  return complete_type_or_maybe_complain (type, value, tf_warning_or_error,
+					  allow_sizeless_p);
 }
 
 \f
@@ -1633,6 +1650,10 @@ cxx_sizeof_or_alignof_type (tree type, e
 	 compute the value, we'll likely end up with SAVE_EXPRs, which
 	 the template substitution machinery does not expect to see.  */
       || (processing_template_decl 
+	  /* Correct for sizeless types, since (a) it's an error
+	     to use them with sizeof and alignof, reported by
+	     c_sizeof_or_alignof_type and (b) this code is geared
+	     around arrays, which are never sizeless.  */
 	  && COMPLETE_TYPE_P (type)
 	  && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST))
     {
@@ -1659,6 +1680,8 @@ cxx_sizeof_nowarn (tree type)
       || VOID_TYPE_P (type)
       || TREE_CODE (type) == ERROR_MARK)
     return size_one_node;
+  /* Correct for sizeless types, whose size should never be measured
+     by the frontend.  */
   else if (!COMPLETE_TYPE_P (type))
     return size_zero_node;
   else
@@ -2109,7 +2132,7 @@ decay_conversion (tree exp,
   if (!CLASS_TYPE_P (type) && cv_qualified_p (type))
     exp = build_nop (cv_unqualified (type), exp);
 
-  if (!complete_type_or_maybe_complain (type, exp, complain))
+  if (!definite_type_or_maybe_complain (type, exp, complain))
     return error_mark_node;
 
   return exp;
@@ -2366,7 +2389,7 @@ build_class_member_access_expr (cp_expr
      complete type).  */
   object_type = TREE_TYPE (object);
   if (!currently_open_class (object_type)
-      && !complete_type_or_maybe_complain (object_type, object, complain))
+      && !definite_type_or_maybe_complain (object_type, object, complain))
     return error_mark_node;
   if (!CLASS_TYPE_P (object_type))
     {
@@ -2902,7 +2925,7 @@ finish_class_member_access_expr (cp_expr
      The type of the first expression shall be "class object" (of a
      complete type).  */
   if (!currently_open_class (object_type)
-      && !complete_type_or_maybe_complain (object_type, object, complain))
+      && !definite_type_or_maybe_complain (object_type, object, complain))
     return error_mark_node;
   if (!CLASS_TYPE_P (object_type))
     {
@@ -3409,6 +3432,7 @@ cp_build_array_ref (location_t loc, tree
 	 address arithmetic on its address.
 	 Likewise an array of elements of variable size.  */
       if (TREE_CODE (idx) != INTEGER_CST
+	  /* Correct for sizeless types, which aren't allowed in arrays.  */
 	  || (COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (array)))
 	      && (TREE_CODE (TYPE_SIZE (TREE_TYPE (TREE_TYPE (array))))
 		  != INTEGER_CST)))
@@ -3453,6 +3477,7 @@ cp_build_array_ref (location_t loc, tree
 	|= (CP_TYPE_VOLATILE_P (type) | TREE_SIDE_EFFECTS (array));
       TREE_THIS_VOLATILE (rval)
 	|= (CP_TYPE_VOLATILE_P (type) | TREE_THIS_VOLATILE (array));
+      /* Correct for sizeless types, which aren't allowed in arrays.  */
       ret = require_complete_type_sfinae (rval, complain);
       protected_set_expr_location (ret, loc);
       if (non_lvalue)
@@ -3554,7 +3579,7 @@ get_member_function_from_ptrfunc (tree *
 
       /* True if we know that the dynamic type of the object doesn't have
 	 virtual functions, so we can assume the PFN field is a pointer.  */
-      nonvirtual = (COMPLETE_TYPE_P (basetype)
+      nonvirtual = (DEFINITE_TYPE_P (basetype)
 		    && !TYPE_POLYMORPHIC_P (basetype)
 		    && resolves_to_fixed_type_p (instance_ptr, 0));
 
@@ -3998,7 +4023,7 @@ convert_arguments (tree typelist, vec<tr
 	  /* Formal parm type is specified by a function prototype.  */
 	  tree parmval;
 
-	  if (!COMPLETE_TYPE_P (complete_type (type)))
+	  if (!DEFINITE_TYPE_P (complete_type (type)))
 	    {
               if (complain & tf_error)
                 {
@@ -4029,7 +4054,7 @@ convert_arguments (tree typelist, vec<tr
 	    /* Don't do ellipsis conversion for __built_in_constant_p
 	       as this will result in spurious errors for non-trivial
 	       types.  */
-	    val = require_complete_type_sfinae (val, complain);
+	    val = require_definite_type_sfinae (val, complain);
 	  else
 	    val = convert_arg_to_ellipsis (val, complain);
 
@@ -5573,6 +5598,7 @@ pointer_diff (location_t loc, tree op0,
   tree restype = ptrdiff_type_node;
   tree target_type = TREE_TYPE (ptrtype);
 
+  /* Can't subtract pointers to sizeless types.  */
   if (!complete_type_or_else (target_type, NULL_TREE))
     return error_mark_node;
 
@@ -5637,6 +5663,8 @@ pointer_diff (location_t loc, tree op0,
     op0 = build2_loc (loc, POINTER_DIFF_EXPR, inttype, op0, op1);
 
   /* This generates an error if op1 is a pointer to an incomplete type.  */
+  /* Correct for sizeless types, which can't be used with pointer
+     arithmetic.  */
   if (!COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (op1))))
     {
       if (complain & tf_error)
@@ -5699,7 +5727,7 @@ build_x_unary_op (location_t loc, enum t
   if (code == ADDR_EXPR
       && TREE_CODE (xarg) != TEMPLATE_ID_EXPR
       && ((CLASS_TYPE_P (TREE_TYPE (xarg))
-	   && !COMPLETE_TYPE_P (complete_type (TREE_TYPE (xarg))))
+	   && !DEFINITE_TYPE_P (complete_type (TREE_TYPE (xarg))))
 	  || (TREE_CODE (xarg) == OFFSET_REF)))
     /* Don't look for a function.  */;
   else
@@ -7552,6 +7580,8 @@ build_reinterpret_cast_1 (tree type, tre
 	  && (complain & tf_warning)
 	  && !VOID_TYPE_P (type)
 	  && TREE_CODE (TREE_TYPE (intype)) != FUNCTION_TYPE
+	  /* Correct for sizeless types, which have no explicit alignment
+	     at the language level.  */
 	  && COMPLETE_TYPE_P (TREE_TYPE (type))
 	  && COMPLETE_TYPE_P (TREE_TYPE (intype))
 	  && min_align_of_type (TREE_TYPE (type))
@@ -8142,7 +8172,7 @@ cp_build_modify_expr (location_t loc, tr
     }
   else
     {
-      lhs = require_complete_type_sfinae (lhs, complain);
+      lhs = require_definite_type_sfinae (lhs, complain);
       if (lhs == error_mark_node)
 	return error_mark_node;
 
@@ -8933,7 +8963,7 @@ convert_for_assignment (tree type, tree
 		  && TYPE_PTR_P (type)
 		  && CLASS_TYPE_P (TREE_TYPE (rhstype))
 		  && CLASS_TYPE_P (TREE_TYPE (type))
-		  && !COMPLETE_TYPE_P (TREE_TYPE (rhstype)))
+		  && !DEFINITE_TYPE_P (TREE_TYPE (rhstype)))
 		inform (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL
 					      (TREE_TYPE (rhstype))),
 			"class type %qT is incomplete", TREE_TYPE (rhstype));
@@ -9821,6 +9851,8 @@ ptr_reasonably_similar (const_tree to, c
 	{
 	  /* When either type is incomplete avoid DERIVED_FROM_P,
 	     which may call complete_type (c++/57942).  */
+	  /* COMPARE_STRICT is correct for sizeless types, since they
+	     cannot have bases.  */
 	  bool b = !COMPLETE_TYPE_P (to) || !COMPLETE_TYPE_P (from);
 	  return comptypes
 	    (TYPE_MAIN_VARIANT (to), TYPE_MAIN_VARIANT (from),
@@ -9982,7 +10014,7 @@ cp_apply_type_quals_to_decl (int type_qu
 
   /* If the type has (or might have) a mutable component, that component
      might be modified.  */
-  if (TYPE_HAS_MUTABLE_P (type) || !COMPLETE_TYPE_P (type))
+  if (TYPE_HAS_MUTABLE_P (type) || !DEFINITE_TYPE_P (type))
     type_quals &= ~TYPE_QUAL_CONST;
 
   c_apply_type_quals_to_decl (type_quals, decl);
Index: gcc/cp/typeck2.c
===================================================================
--- gcc/cp/typeck2.c	2018-09-25 08:03:44.163263685 +0100
+++ gcc/cp/typeck2.c	2018-10-15 14:13:37.864189824 +0100
@@ -177,7 +177,7 @@ static GTY (()) hash_table<abstract_type
 
 static int abstract_virtuals_error_sfinae (tree, tree, abstract_class_use, tsubst_flags_t);
 
-/* This function is called after TYPE is completed, and will check if there
+/* This function is called after TYPE is fully-defined, and will check if there
    are pending declarations for which we still need to verify the abstractness
    of TYPE, and emit a diagnostic (through abstract_virtuals_error) if TYPE
    turned out to be incomplete.  */
@@ -188,7 +188,7 @@ complete_type_check_abstract (tree type)
   struct pending_abstract_type *pat;
   location_t cur_loc = input_location;
 
-  gcc_assert (COMPLETE_TYPE_P (type));
+  gcc_assert (DEFINITE_TYPE_P (type));
 
   if (!abstract_pending_vars)
     return;
@@ -268,7 +268,7 @@ abstract_virtuals_error_sfinae (tree dec
      so that we can check again once it is completed. This makes sense
      only for objects for which we have a declaration or at least a
      name.  */
-  if (!COMPLETE_TYPE_P (type) && (complain & tf_error))
+  if (!DEFINITE_TYPE_P (type) && (complain & tf_error))
     {
       struct pending_abstract_type *pat;
 
@@ -1032,7 +1032,7 @@ digest_init_r (tree type, tree init, int
 
   /* We must strip the outermost array type when completing the type,
      because the its bounds might be incomplete at the moment.  */
-  if (!complete_type_or_maybe_complain (code == ARRAY_TYPE
+  if (!definite_type_or_maybe_complain (code == ARRAY_TYPE
 					? TREE_TYPE (type) : type, NULL_TREE,
 					complain))
     return error_mark_node;
@@ -1970,6 +1970,8 @@ build_m_component_ref (tree datum, tree
   type = TYPE_PTRMEM_POINTED_TO_TYPE (ptrmem_type);
   ctype = complete_type (TYPE_PTRMEM_CLASS_TYPE (ptrmem_type));
 
+  /* Correct for sizeless types, which don't have bases and can't be
+     used as bases.  */
   if (!COMPLETE_TYPE_P (ctype))
     {
       if (!same_type_p (ctype, objtype))
@@ -2167,7 +2169,7 @@ build_functional_cast (tree exp, tree pa
 
      then the slot being initialized will be filled in.  */
 
-  if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
+  if (!definite_type_or_maybe_complain (type, NULL_TREE, complain))
     return error_mark_node;
   if (abstract_virtuals_error_sfinae (ACU_CAST, type, complain))
     return error_mark_node;
@@ -2248,6 +2250,7 @@ add_exception_specifier (tree list, tree
 	 but it seems more reasonable to only require this on definitions
 	 and calls.  So just give a pedwarn at this point; we will give an
 	 error later if we hit one of those two cases.  */
+      /* Correct for sizeless types, which can't be thrown.  */
       if (!COMPLETE_TYPE_P (complete_type (core)))
 	diag_type = DK_PEDWARN; /* pedwarn */
     }
@@ -2359,6 +2362,7 @@ require_complete_eh_spec_types (tree fnt
        raises = TREE_CHAIN (raises))
     {
       tree type = TREE_VALUE (raises);
+      /* Correct for sizeless types, which can't be thrown.  */
       if (type && !COMPLETE_TYPE_P (type))
 	{
 	  if (decl)
Index: gcc/testsuite/g++.dg/ext/sizeless-1.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-1.C	2018-10-15 14:13:37.864189824 +0100
@@ -0,0 +1,793 @@
+// { dg-options "-Wconditionally-supported -Wdelete-incomplete -Wclass-memaccess" }
+
+typedef __SIZE_TYPE__ size_t;
+
+inline void *operator new (size_t, void *__p) throw() { return __p; }
+
+// Invalid mixtures of tags.
+
+struct initially_struct;
+__sizeless_struct initially_struct; // { dg-error {'__sizeless_struct' tag used in naming 'struct initially_struct'} }
+
+class initially_class;
+__sizeless_struct initially_class; // { dg-error {'__sizeless_struct' tag used in naming 'struct initially_class'} }
+
+union initially_union;
+__sizeless_struct initially_union; // { dg-error {'__sizeless_struct' tag used in naming 'union initially_union'} }
+
+__sizeless_struct initially_sizeless;
+__sizeless_struct initially_sizeless;
+struct initially_sizeless; // { dg-error {'struct' tag used in naming '__sizeless_struct initially_sizeless'} }
+class initially_sizeless; // { dg-error {'class' tag used in naming '__sizeless_struct initially_sizeless'} }
+union initially_sizeless; // { dg-error {'union' tag used in naming '__sizeless_struct initially_sizeless'} }
+enum initially_sizeless; // { dg-error {'initially_sizeless' referred to as enum} }
+
+// Simple __sizeless_struct definitions.
+
+__sizeless_struct ta { int a; };
+__sizeless_struct tb { int a; }; // { dg-message {note: class type 'tb' is incomplete} "don't want this message" { xfail *-*-* } }
+typedef __sizeless_struct { int a; } tc;
+__sizeless_struct td { td (int); void f (); };
+__sizeless_struct te { te *f (); int x; };
+__sizeless_struct tf { ~tf (); tf &operator= (const tf &); };
+__sizeless_struct tg
+{
+#if __cplusplus >= 201103L
+  tg () = default;
+#else
+  tg () {}
+#endif
+  tg (const tg &);
+
+  static int exists;
+};
+__sizeless_struct th
+{
+#if __cplusplus >= 201103L
+  th &operator= (const th &) = delete;
+#else
+ private:
+  th &operator= (const th &);
+#endif
+ public:
+  operator int () const;
+  tb *operator& () const;
+
+  typedef int my_int;
+};
+
+__sizeless_struct ti {
+  int i[2];
+};
+
+__sizeless_struct empty {};
+
+__sizeless_struct range {
+  ta *const *begin () const { return a; }
+  ta *const *end () const { return a + 4; }
+  ta *a[4];
+};
+
+// Invalid redefinitions.
+
+__sizeless_struct redef1 { int x; };
+__sizeless_struct redef1 { int y; }; // { dg-error {redefinition of '__sizeless_struct redef1'} }
+
+__sizeless_struct indestructible {
+#if __cplusplus >= 201103L
+  ~indestructible () = delete;
+#endif
+};
+
+#if __cplusplus >= 201103L
+__sizeless_struct sizeless_nsdmi {
+  ta ta1 = {};
+  ta ta2 = [=] () { return ta1; }();
+};
+#endif
+
+// Sizeless objects with global scope.
+
+ta global_ta; // { dg-error {sizeless variable 'ta global_ta' cannot have static storage duration} }
+static ta local_ta; // { dg-error {sizeless variable 'ta local_ta' cannot have static storage duration} }
+extern ta extern_ta; // { dg-error {sizeless variable 'ta extern_ta' cannot have static storage duration} }
+__thread ta tls_ta; // { dg-error {sizeless variable 'ta tls_ta' cannot have thread-local storage duration} }
+
+// Inheriting sizeless types
+
+struct inherit_ta1 : public ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+struct inherit_ta2 : protected ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+struct inherit_ta3 : private ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+struct inherit_ta4 : virtual public ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+class inherit_ta5 : public ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+__sizeless_struct inherit_ta6 : public ta {}; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+
+// Inheritance in sizeless types.
+
+struct sized_base { int a; };
+
+__sizeless_struct inherit_sized1 : public sized_base {}; // { dg-error {sizeless type 'inherit_sized1' cannot have base classes} }
+__sizeless_struct inherit_sized2 : protected sized_base {}; // { dg-error {sizeless type 'inherit_sized2' cannot have base classes} }
+__sizeless_struct inherit_sized3 : private sized_base {}; // { dg-error {sizeless type 'inherit_sized3' cannot have base classes} }
+__sizeless_struct inherit_sized4 : virtual public sized_base {}; // { dg-error {sizeless type 'inherit_sized4' cannot have base classes} }
+
+// Virtual methods in sizeless types.
+
+__sizeless_struct with_virtuals {
+  virtual ~with_virtuals (); // { dg-error {sizeless types cannot have virtual functions} }
+  virtual void f1 (); // { dg-error {sizeless types cannot have virtual functions} }
+  virtual void f2 () = 0; // { dg-error {sizeless types cannot have virtual functions} }
+  virtual void f3 () const; // { dg-error {sizeless types cannot have virtual functions} }
+#if __cplusplus >= 201103L
+  virtual void f4 () override; // { dg-error {sizeless types cannot have virtual functions} "" { target c++11 } }
+  void f5 () override; // { dg-error {'void with_virtuals::f5\(\)' marked 'override', but does not override} "" { target c++11 } }
+#endif
+};
+
+// Sizeless fields.
+
+struct struct1 {
+  ta a; // { dg-error {field 'a' has incomplete type} }
+  __sizeless_struct { int i; }; // { dg-error {'struct struct1' cannot have anonymous sizeless fields} }
+  __sizeless_struct { int i; } named; // { dg-error {field 'named' has incomplete type} }
+
+  __sizeless_struct nested { int i; };
+};
+
+union union1 {
+  ta a; // { dg-error {field 'a' has incomplete type} }
+  __sizeless_struct { int i; }; // { dg-error {'union union1' cannot have anonymous sizeless fields} }
+  __sizeless_struct { int i; } named; // { dg-error {field 'named' has incomplete type} }
+
+  __sizeless_struct nested { int i; };
+};
+
+// Sizeless fields in templated structures.
+
+template<typename T>
+struct templated_struct1 {
+  ta a; // { dg-error {field 'a' has incomplete type} }
+  __sizeless_struct { int i; }; // { dg-error {'struct templated_struct1<T>' cannot have anonymous sizeless fields} }
+  __sizeless_struct { int i; } named;
+
+  __sizeless_struct nested { int i; };
+};
+
+template<typename T>
+struct templated_struct2 {
+  __sizeless_struct { int i; } named; // { dg-error {'templated_struct2<T>::named' has incomplete type} }
+};
+
+template struct templated_struct2<int>;
+
+template<typename T>
+struct templated_struct3 {
+  T a; // { dg-error {'templated_struct3<T>::a' has incomplete type} }
+};
+
+template struct templated_struct3<ta>;
+
+// Nested aggregates.
+
+__sizeless_struct multilevel {
+  ta a1, a2;
+  struct { int i1; };
+  union { int i2; };
+  __sizeless_struct { ta a3; };
+};
+
+template<int N>
+__sizeless_struct templated_sizeless1 {
+  int x[N]; // { dg-error {size of array is negative} }
+};
+
+typedef __sizeless_struct templated_sizeless1<1> template_sizeless1_typedef;
+template __sizeless_struct templated_sizeless1<2>;
+template __sizeless_struct templated_sizeless1<-1>; // { dg-message {required from here} }
+
+#if __cplusplus >= 201103L
+template<int N> using typedef_sizeless1 = ta;
+template<int N> using typedef_sizeless1 = ta; // { dg-error {redefinition of 'template<int N> using typedef_sizeless1 = ta'} "" { target c++11 } }
+#endif
+
+template<typename T1, typename T2>
+__sizeless_struct templated_sizeless2 {};
+template<typename T1, typename T2>
+__sizeless_struct templated_sizeless2<T1, T2 *> {};
+void use_templated_sizeless2 () { templated_sizeless2<int *, int *> x; }
+template<typename T1, typename T2>
+__sizeless_struct templated_sizeless2<T1 *, T2> {}; // { dg-error {declaration of '__sizeless_struct templated_sizeless2<T1\*, T2>' ambiguates earlier template instantiation for '__sizeless_struct templated_sizeless2<int\*, int\*>'} }
+
+template<typename T>
+__sizeless_struct templated_sizeless3
+{
+  typename T::nested member;
+};
+template __sizeless_struct templated_sizeless3<struct1>;
+
+template<typename T>
+__sizeless_struct templated_sizeless4
+{
+  typedef T type;
+};
+
+template<typename T>
+struct typename_static_member_user
+{
+  static typename templated_sizeless4<T>::type static_member;
+};
+template struct typename_static_member_user<int>;
+
+template<typename T1>
+__sizeless_struct templated_sizeless5
+{
+  template<typename T2>
+  __sizeless_struct nested
+  {
+    T1 x;
+    T2 y;
+  };
+};
+
+template __sizeless_struct templated_sizeless5<int>::nested<float>;
+
+template<>
+template<typename T2>
+__sizeless_struct templated_sizeless5<int>::nested // { dg-error {specialization 'templated_sizeless5<int>::nested<T2>' after instantiation 'templated_sizeless5<int>::nested<float>'} }
+{
+};
+
+// Use of 'friend'.
+
+class friend_of_td
+{
+  friend void td::f ();
+  int x;
+};
+
+template<typename T>
+class templated_friend_of_td
+{
+  friend void td::f ();
+  int x;
+};
+template class templated_friend_of_td<int>;
+
+// Pointers to sizeless types.
+
+ta *global_ta_ptr;
+
+// Types nested in sizeless types.
+
+typename th::my_int global_int1;
+typename th::my_float global_float1; // { dg-error {'my_float' in '__sizeless_struct th' does not name a type} }
+
+// Static members of sizeless types.
+
+int ta::a; // { dg-error {'int ta::a' is not a static data member of '__sizeless_struct ta'} }
+int tg::exists;
+int tg::doesnt_exist; // { dg-error {'int tg::doesnt_exist' is not a static data member of '__sizeless_struct tg'} }
+
+// Member pointers.
+
+int ta::*ta_memptr1;
+int ta::*ta_memptr2 = &ta::a; // { dg-error {cannot take address of member of sizeless type 'ta'} }
+int ta::*ta_memptr3 = &ta::ta::a; // { dg-error {cannot take address of member of sizeless type 'ta'} }
+void (td::*td_memptr1) (void);
+void (td::*td_memptr2) (void) = &td::f;
+void (td::*td_memptr3) (void) = &td::td::f;
+
+// Sizeless arguments and return values.
+
+void ext_consume_ta (ta);
+void ext_consume_const_int_ref (const int &);
+void ext_consume_varargs (int, ...);
+ta ext_produce_ta ();
+
+// Sizeless types in throw specifications.
+
+#if __cplusplus < 201103L
+void thrower1 () throw (ta); // { dg-warning {invalid use of incomplete type '__sizeless_struct ta'} "" { target c++98_only } }
+void thrower2 () throw (ta); // { dg-warning {invalid use of incomplete type '__sizeless_struct ta'} "" { target c++98_only } }
+void thrower3 () throw (ta); // { dg-warning {invalid use of incomplete type '__sizeless_struct ta'} "" { target c++98_only } }
+#endif
+
+#if __cplusplus >= 201103L
+constexpr int
+constexpr_fn1 ()
+{
+  return ({ ta ta1 = { 100 }; ta1.a; }); // { dg-error {variable 'ta1' of non-literal type 'ta' in 'constexpr' function} "" { target c++11 } }
+}
+
+constexpr int
+constexpr_fn2 (ta ta1) // { dg-error {invalid type for parameter 1} "" { target c++11 } }
+{
+  return ta1.a;
+}
+
+constexpr int
+constexpr_fn3 (empty empty1) // { dg-error {invalid type for parameter 1} "" { target c++11 } }
+{
+  return 0;
+}
+
+constexpr ta
+constexpr_fn4 () // { dg-error {invalid return type 'ta' of 'constexpr' function} "" { target c++11 } }
+{
+  return ta ();
+}
+#endif
+
+// Main tests for statements and expressions.
+
+void
+statements (int n)
+{
+  // Local declarations.
+
+  ta ta1, ta2;
+  tb tb1;
+  td td1 (1);
+  td td2; // { dg-error {no matching function for call to 'td::td\(\)'} }
+  tf tf1;
+  tg tg1;
+  th th1;
+  { templated_sizeless1<2> x; }
+  { indestructible x; } // { dg-error {use of deleted function 'indestructible::~indestructible\(\)'} "" { target c++11 } }
+  volatile ta volatile_ta1;
+#if __cplusplus >= 201103L
+  sizeless_nsdmi nsdmi1;
+#endif
+
+  // Layout queries.
+
+  sizeof (ta); // { dg-error {invalid application of 'sizeof' to incomplete type} }
+  sizeof (ta1); // { dg-error {invalid application of 'sizeof' to incomplete type} }
+  sizeof (ext_produce_ta ()); // { dg-error {invalid application of 'sizeof' to incomplete type} }
+  __alignof (ta); // { dg-error {invalid application of '__alignof__' to incomplete type} }
+  __alignof (ext_produce_ta ()); // { dg-error {invalid application of '__alignof__' to incomplete type} }
+
+  // Initialization.
+
+  int init_int1 = ta1; // { dg-error {cannot convert 'ta' to 'int' in initialization} }
+  int init_int2 = { ta1 }; // { dg-error {cannot convert 'ta' to 'int' in initialization} }
+  int init_int3 = th1;
+
+  ta init_ta1 (ta1);
+  ta init_ta2 (tb1); // { dg-error {no matching function for call to 'ta::ta\(tb&\)'} }
+  ta init_ta3 = ta1;
+  ta init_ta4 = tb1; // { dg-error {conversion from 'tb' to non-scalar type 'ta' requested} }
+  ta init_ta5 = 0; // { dg-error {conversion from 'int' to non-scalar type 'ta' requested} }
+  ta init_ta6 = {};
+  ta init_ta7 = { 0 };
+  ta init_ta8 = { n };
+  ta init_ta9 = { 0, 1 }; // { dg-error {too many initializers for 'ta'} }
+  ta init_ta10 = { tb1 }; // { dg-error {cannot convert 'tb' to 'int' in initialization} }
+
+  td init_td1 (td1);
+  td init_td2 (1, 2); // { dg-error {no matching function for call to 'td::td\(int, int\)'} }
+  td init_td3 (ta1); // { dg-error {no matching function for call to 'td::td\(ta&\)'} }
+  td init_td4 = {}; // { dg-error {could not convert} }
+		    // { dg-error {in C\+\+98 'init_td4' must be initialized by constructor, not by '{...}'} "" { target c++98_only } .-1 }
+  td init_td5 = { 1 }; // { dg-error {in C\+\+98 'init_td5' must be initialized by constructor, not by '{...}'} "" { target c++98_only } }
+  td init_td6 = { 1, 2 }; // { dg-error {could not convert} }
+			  // { dg-error {in C\+\+98 'init_td6' must be initialized by constructor, not by '{...}'} "" { target c++98_only } .-1 }
+  td init_td7 = { init_td1 }; // { dg-error {in C\+\+98 'init_td7' must be initialized by constructor, not by '{...}'} "" { target c++98_only } }
+
+  tg init_tg1 (tg1);
+  tg init_tg2 = { init_tg1 }; // { dg-error {in C\+\+98 'init_tg2' must be initialized by constructor, not by '{...}'} "" { target c++98_only } }
+  tg init_tg3 = { ta1 }; // { dg-error {could not convert} }
+			 // { dg-error {in C\+\+98 'init_tg3' must be initialized by constructor, not by '{...}'} "" { target c++98_only } .-1 }
+  tg init_tg4 = {}; // { dg-error {in C\+\+98 'init_tg4' must be initialized by constructor, not by '{...}'} "" { target c++98_only } }
+
+  multilevel init_multilevel1 = { ta1, ta1, 1, 2, ta1 };
+  multilevel init_multilevel2 = { 1, 2, 3, 4, 5 };
+
+  // Constructor calls.
+
+  (0, ta ());
+  (0, ta (0)); // { dg-error {no matching function for call to 'ta::ta\(int\)'} }
+  (0, ta (ta1));
+  (0, ta (tb1)); // { dg-error {no matching function for call to 'ta::ta\(tb\&\)'} }
+
+  (0, td ()); // { dg-error {no matching function for call to 'td::td\(\)'} }
+  (0, td (1));
+  (0, td (td1));
+
+  // constexprs.
+
+#if __cplusplus >= 201103L
+  constexpr ta constexpr_ta1 = {}; // { dg-error {the type 'const ta' of 'constexpr' variable 'constexpr_ta1' is not literal} "" { target c++11 } }
+  constexpr empty constexpr_empty1 = {}; // { dg-error {the type 'const empty' of 'constexpr' variable 'constexpr_empty1' is not literal} "" { target c++11 } }
+#endif
+
+  // Lvalue reference binding.
+
+  ta &lvalue_ref_ta1 = ta1;
+  ta &lvalue_ref_ta2 = ext_produce_ta (); // { dg-error {cannot bind non-const lvalue reference of type 'ta&' to an rvalue of type 'ta'} }
+  ta &lvalue_ref_ta3 = tb1; // { dg-error {invalid initialization of reference of type 'ta&' from expression of type 'tb'} }
+
+  const ta &const_lvalue_ref_ta1 = ta1;
+  const ta &const_lvalue_ref_ta2 = ext_produce_ta ();
+  const ta &const_lvalue_ref_ta3 = tb1; // { dg-error {invalid initialization of reference of type 'const ta&' from expression of type 'tb'} }
+
+  td &lvalue_ref_td1 = 1; // { dg-error {cannot bind non-const lvalue reference of type 'td&' to an rvalue of type 'td'} }
+  td &lvalue_ref_td2 = ta1; // { dg-error {invalid initialization of reference of type 'td&' from expression of type 'ta'} }
+
+  const td &const_lvalue_ref_td1 = 1;
+  const td &const_lvalue_ref_td2 = ta1; // { dg-error {invalid initialization of reference of type 'const td&' from expression of type 'ta'} }
+
+  // Compound literals.
+
+  (int) { ta1 }; // { dg-error {cannot convert 'ta' to 'int' in initialization} }
+
+  // Assignment.
+
+  n = ta1; // { dg-error {cannot convert 'ta' to 'int' in assignment} }
+
+  ta1 = 0; // { dg-error {no match for 'operator=' in 'ta1 = 0' \(operand types are 'ta' and 'int'\)} }
+  ta1 = ta2;
+  ta1 = tb1; // { dg-error {no match for 'operator=' in 'ta1 = tb1' \(operand types are 'ta' and 'tb'\)} }
+
+  td1 = 0;
+  td1 = init_td1;
+  td1 = ta1; // { dg-error {no match for 'operator=' in 'td1 = ta1' \(operand types are 'td' and 'ta'\)} }
+
+  th1 = th1; // { dg-error {'th& th::operator=\(const th&\)' is private within this context} "" { target c++98_only } }
+	     // { dg-error {use of deleted function 'th& th::operator=\(const th&\)'} "" { target c++11 } .-1 }
+
+  // Casting.
+
+  (ta) ta1;
+  (void) ta1;
+  (void) volatile_ta1;
+  (void) *&volatile_ta1;
+
+  // Addressing and dereferencing.
+
+  ta *ta_ptr = &ta1;
+  tb *tb_ptr = &th1;
+  th *th_ptr = &th1; // { dg-error {cannot convert 'tb\*' to 'th\*' in initialization} }
+
+  // Pointer arithmetic.
+
+  ++ta_ptr; // { dg-error {cannot increment a pointer to incomplete type 'ta'} }
+  --ta_ptr; // { dg-error {cannot decrement a pointer to incomplete type 'ta'} }
+  ta_ptr++; // { dg-error {cannot increment a pointer to incomplete type 'ta'} }
+  ta_ptr--; // { dg-error {cannot decrement a pointer to incomplete type 'ta'} }
+  ta_ptr += 0; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta_ptr += 1; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta_ptr -= 0; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta_ptr -= 1; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta_ptr - ta_ptr; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta &ta_ref1 = ta_ptr[0]; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  ta &ta_ref2 = ta_ptr[1]; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+
+  // New and delete.
+
+  new ta; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  new ta (); // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  new td (1); // { dg-error {invalid use of incomplete type '__sizeless_struct td'} }
+  new tg (); // { dg-error {invalid use of incomplete type '__sizeless_struct tg'} }
+
+  new (global_ta_ptr) ta; // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+  new (global_ta_ptr) ta (); // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+
+  delete ta_ptr; // { dg-error {cannot delete objects of sizeless type 'ta'} }
+  delete[] ta_ptr; // { dg-error {cannot delete objects of sizeless type 'ta'} }
+
+  // Member access.
+
+  ta1.a = 1;
+  ta1.b = 1; // { dg-error {'__sizeless_struct ta' has no member named 'b'} }
+  ta_ptr->a = 1;
+  (void) &ta::b; // { dg-error {'b' is not a member of 'ta'} }
+
+  // Unary vector arithmetic.
+
+  __real ta1; // { dg-error {no match for '__real__' in '__real__ ta1'} }
+  __imag ta1; // { dg-error {no match for '__imag__' in '__imag__ ta1'} }
+
+  // Conditional expressions.
+
+  0 ? ta1 : ta1;
+
+  // Function arguments.
+
+  ext_consume_ta (ta1);
+  ext_consume_ta (tb1); // { dg-error {could not convert 'tb1' from 'tb' to 'ta'} }
+  ext_consume_const_int_ref (ta1); // { dg-error {invalid initialization of reference of type 'const int&' from expression of type 'ta'} }
+  ext_consume_const_int_ref (th1);
+  ext_consume_varargs (ta1); // { dg-error {cannot convert 'ta' to 'int'} }
+  ext_consume_varargs (1, ta1);
+  ext_consume_varargs (1, tg1); // { dg-warning {passing objects of non-trivially-copyable type '__sizeless_struct tg'} }
+  (td1.*td_memptr3) ();
+
+  // Function returns.
+
+  ext_produce_ta ();
+  ta1 = ext_produce_ta ();
+  tb1 = ext_produce_ta (); // { dg-error {no match for 'operator=' in 'tb1 = ext_produce_ta\(\)'} }
+
+  // Use of 'auto'.
+
+#if __cplusplus >= 201103L
+  auto auto_ta1 = ta1;
+  auto auto_ta2 = ext_produce_ta ();
+#endif
+
+  // Varargs processing.
+
+  __builtin_va_list valist;
+  __builtin_va_arg (valist, ta);
+
+  // Use in atomics.
+
+  __sync_lock_test_and_set (global_ta_ptr, 0); // { dg-error {operand type '[^']*'[^\n]* is incompatible with argument 1} }
+
+  __builtin_memset (&ta1, 0, 100);
+  __builtin_memset (&td1, 0, 100); // { dg-warning {clearing an object of non-trivial type '__sizeless_struct td'} }
+  __builtin_memset (&tg1, 0, 100); // { dg-warning {clearing an object of non-trivial type '__sizeless_struct tg'} }
+
+  // Other built-ins.
+
+  __builtin_launder (ta1); // { dg-error {non-pointer argument to '__builtin_launder'} }
+
+  // Lambdas.
+
+#if __cplusplus >= 201103L
+  [ta1] () {}; // { dg-error {capture by copy of incomplete type 'ta'} "" { target c++11 } }
+  [=] () { &ta1; }; // { dg-error {capture by copy of incomplete type 'ta'} "" { target c++11 } }
+  [&ta1] () { ta1 = ta2; }; // { dg-error {'ta2' is not captured} "" { target c++11 } }
+  [&ta1, &ta2] () { ta1 = ta2; };
+  [&] () { ta1 = ta2; };
+  [] () { return ext_produce_ta (); } ();
+#endif
+
+  // Exceptions.
+
+  throw td (1); // { dg-error {invalid use of incomplete type '__sizeless_struct td'} }
+  try {} catch (ta x) {} // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} }
+#if __cplusplus < 201103L
+  thrower2 (); // { dg-error {call to function 'void thrower2\(\)' which throws incomplete type '__sizeless_struct ta'} "" { target c++98_only } }
+#endif
+
+  // Ranges.
+
+#if __cplusplus >= 201103L
+  for (auto x : empty ()) {} // { dg-error {'begin' was not declared in this scope} "" { target c++11 } }
+			     // { dg-error {'end' was not declared in this scope} "" { target c++11 } .-1 }
+  for (auto x : range ()) {}
+#endif
+
+  // Use in goto.
+
+  goto *ta1; // { dg-error {cannot convert 'ta1' from type 'ta' to type 'void\*'} }
+
+  // Use in traits.  Doesn't use static_assert so that tests work with C++98.
+
+  { typedef int f[__has_nothrow_assign (ta) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_assign (td) ? 1 : -1]; }
+  { typedef int f[!__has_nothrow_assign (tf) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_assign (tg) ? 1 : -1]; }
+#if __cplusplus >= 201103L
+  { typedef int f[__has_nothrow_assign (th) ? 1 : -1]; }
+#else
+  { typedef int f[!__has_nothrow_assign (th) ? 1 : -1]; }
+#endif
+
+  { typedef int f[__has_trivial_assign (ta) ? 1 : -1]; }
+  { typedef int f[__has_trivial_assign (td) ? 1 : -1]; }
+  { typedef int f[!__has_trivial_assign (tf) ? 1 : -1]; }
+  { typedef int f[__has_trivial_assign (tg) ? 1 : -1]; }
+#if __cplusplus >= 201103L
+  { typedef int f[__has_trivial_assign (th) ? 1 : -1]; }
+#else
+  { typedef int f[!__has_trivial_assign (th) ? 1 : -1]; }
+#endif
+
+  { typedef int f[__has_nothrow_constructor (ta) ? 1 : -1]; }
+  { typedef int f[!__has_nothrow_constructor (td) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_constructor (tf) ? 1 : -1]; }
+#if __cplusplus >= 201103L
+  { typedef int f[__has_nothrow_constructor (tg) ? 1 : -1]; }
+#else
+  { typedef int f[!__has_nothrow_constructor (tg) ? 1 : -1]; }
+#endif
+  { typedef int f[__has_nothrow_constructor (th) ? 1 : -1]; }
+
+  { typedef int f[__has_trivial_constructor (ta) ? 1 : -1]; }
+  { typedef int f[!__has_trivial_constructor (td) ? 1 : -1]; }
+  { typedef int f[__has_trivial_constructor (tf) ? 1 : -1]; }
+#if __cplusplus >= 201103L
+  { typedef int f[__has_trivial_constructor (tg) ? 1 : -1]; }
+#else
+  { typedef int f[!__has_trivial_constructor (tg) ? 1 : -1]; }
+#endif
+  { typedef int f[__has_trivial_constructor (th) ? 1 : -1]; }
+
+  { typedef int f[__has_nothrow_copy (ta) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_copy (td) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_copy (tf) ? 1 : -1]; }
+  { typedef int f[!__has_nothrow_copy (tg) ? 1 : -1]; }
+  { typedef int f[__has_nothrow_copy (th) ? 1 : -1]; }
+
+  { typedef int f[__has_trivial_copy (ta) ? 1 : -1]; }
+  { typedef int f[__has_trivial_copy (td) ? 1 : -1]; }
+  { typedef int f[__has_trivial_copy (tf) ? 1 : -1]; }
+  { typedef int f[!__has_trivial_copy (tg) ? 1 : -1]; }
+  { typedef int f[__has_trivial_copy (th) ? 1 : -1]; }
+
+  { typedef int f[__has_trivial_destructor (ta) ? 1 : -1]; }
+  { typedef int f[__has_trivial_destructor (td) ? 1 : -1]; }
+  { typedef int f[!__has_trivial_destructor (tf) ? 1 : -1]; }
+  { typedef int f[__has_trivial_destructor (tg) ? 1 : -1]; }
+  { typedef int f[__has_trivial_destructor (th) ? 1 : -1]; }
+
+  { typedef int f[__has_unique_object_representations (ta) ? 1 : -1]; }
+  { typedef int f[!__has_virtual_destructor (ta) ? 1 : -1]; }
+  { typedef int f[!__is_abstract (ta) ? 1 : -1]; }
+  { typedef int f[__is_aggregate (ta) ? 1 : -1]; }
+  { typedef int f[__is_base_of (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_base_of (tb, ta) ? 1 : -1]; }
+  { typedef int f[__is_class (ta) ? 1 : -1]; }
+  { typedef int f[!__is_empty (ta) ? 1 : -1]; }
+  { typedef int f[!__is_enum (ta) ? 1 : -1]; }
+  { typedef int f[!__is_final (ta) ? 1 : -1]; }
+  { typedef int f[__is_pod (ta) ? 1 : -1]; }
+  { typedef int f[!__is_polymorphic (ta) ? 1 : -1]; }
+  { typedef int f[__is_same_as (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_same_as (ta, int) ? 1 : -1]; }
+  { typedef int f[__is_trivial (ta) ? 1 : -1]; }
+  { typedef int f[!__is_union (ta) ? 1 : -1]; }
+  { typedef int f[!__is_literal_type (ta) ? 1 : -1]; }
+
+  { typedef int f[__is_trivially_copyable (ta) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_copyable (tg) ? 1 : -1]; }
+
+  { typedef int f[!__is_trivially_assignable (ta, int) ? 1 : -1]; }
+  { typedef int f[__is_trivially_assignable (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_assignable (ta, tb) ? 1 : -1]; }
+
+  { typedef int f[!__is_trivially_assignable (td, int) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_assignable (td, ta) ? 1 : -1]; }
+  { typedef int f[__is_trivially_assignable (td, td) ? 1 : -1]; }
+
+  { typedef int f[!__is_trivially_assignable (tf, tf) ? 1 : -1]; }
+
+  { typedef int f[!__is_trivially_assignable (th, th) ? 1 : -1]; } // { dg-error {'th& th::operator=\(const th&\)' is private within this context} "" { target c++98_only } }
+
+  { typedef int f[!__is_assignable (ta, int) ? 1 : -1]; }
+  { typedef int f[__is_assignable (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_assignable (ta, tb) ? 1 : -1]; }
+
+  { typedef int f[__is_assignable (td, int) ? 1 : -1]; }
+  { typedef int f[!__is_assignable (td, ta) ? 1 : -1]; }
+  { typedef int f[__is_assignable (td, td) ? 1 : -1]; }
+
+  { typedef int f[__is_assignable (tf, tf) ? 1 : -1]; }
+
+#if __cplusplus >= 201103L
+  { typedef int f[!__is_assignable (th, th) ? 1 : -1]; }
+#else
+  { typedef int f[__is_assignable (th, th) ? 1 : -1]; } // { dg-error {'th& th::operator=\(const th&\)' is private within this context} "" { target c++98_only } }
+#endif
+
+  { typedef int f[__is_trivially_constructible (ta) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_constructible (ta, int) ? 1 : -1]; }
+  { typedef int f[__is_trivially_constructible (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_constructible (ta, tb) ? 1 : -1]; }
+
+  { typedef int f[!__is_trivially_constructible (td) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_constructible (td, int) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_constructible (td, int, int) ? 1 : -1]; }
+  { typedef int f[!__is_trivially_constructible (td, ta) ? 1 : -1]; }
+  { typedef int f[__is_trivially_constructible (td, td) ? 1 : -1]; }
+
+#if __cplusplus >= 201103L
+  { typedef int f[__is_trivially_constructible (tg) ? 1 : -1]; }
+#else
+  { typedef int f[!__is_trivially_constructible (tg) ? 1 : -1]; }
+#endif
+  { typedef int f[!__is_trivially_constructible (tg, tg) ? 1 : -1]; }
+
+  { typedef int f[__is_constructible (ta) ? 1 : -1]; }
+  { typedef int f[!__is_constructible (ta, int) ? 1 : -1]; }
+  { typedef int f[__is_constructible (ta, ta) ? 1 : -1]; }
+  { typedef int f[!__is_constructible (ta, tb) ? 1 : -1]; }
+
+  { typedef int f[!__is_constructible (td) ? 1 : -1]; }
+  { typedef int f[__is_constructible (td, int) ? 1 : -1]; }
+  { typedef int f[!__is_constructible (td, int, int) ? 1 : -1]; }
+  { typedef int f[!__is_constructible (td, ta) ? 1 : -1]; }
+  { typedef int f[__is_constructible (td, td) ? 1 : -1]; }
+
+  { typedef int f[__is_constructible (tg) ? 1 : -1]; }
+  { typedef int f[__is_constructible (tg, tg) ? 1 : -1]; }
+}
+
+// Member function definitions
+
+void
+td::f ()
+{
+  friend_of_td friend1;
+  friend1.x = 0;
+  templated_friend_of_td<int> friend2;
+  friend2.x = 0;
+}
+
+te *
+te::f ()
+{
+  x += 1;
+  if (x & 3)
+    f ();
+  return this;
+}
+
+void te::no_such_fn () {} // { dg-error {no declaration matches 'void te::no_such_fn\(\)'} }
+
+// Function parameters in definitions.
+
+void
+unnamed_st1 (ta)
+{
+}
+
+void
+named_st1 (ta param1)
+{
+  ta ta1 = param1;
+}
+
+// Function return values in definitions.
+
+ta
+ret_st1 (ta param)
+{
+  return param;
+}
+
+ta
+bad_ret_st1 (tb param)
+{
+  return param; // { dg-error {could not convert 'param' from 'tb' to 'ta'} }
+}
+
+#if __cplusplus >= 201103L
+ti
+ret_ti ()
+{
+  return { 1, 2 };
+}
+#endif
+
+#if __cplusplus < 201103L
+void thrower3 () throw (ta) {} // { dg-error {invalid use of incomplete type '__sizeless_struct ta'} "" { target c++98_only } }
+			       // { dg-warning {invalid use of incomplete type '__sizeless_struct ta'} "" { target c++98_only } .-1 }
+#endif
+
+// Using "auto" as a return type.
+
+#if __cplusplus >= 201402L
+auto auto_ret_ta (ta *ptr) { return *ptr; }
+const auto &auto_ret_const_ta_ref (ta *ptr) { return *ptr; }
+auto &auto_ret_ta_ref (ta *ptr) { return *ptr; }
+auto &&auto_ret_ta_rvalue_ref (ta *ptr) { return *ptr; }
+#endif
+
+// Invalid destructor calls.
+
+template<typename T>
+void
+destroy_via_x (T t)
+{
+  t.T::~X(); // { dg-error {no type named 'X' in '__sizeless_struct ta'} }
+}
+
+void
+call_destroy_via_x ()
+{
+  destroy_via_x (ta ());
+}
Index: gcc/testsuite/g++.dg/ext/sizeless-2.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-2.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,2 @@
+__sizeless_struct s {};
+const int foo = __is_literal_type (s);
Index: gcc/testsuite/g++.dg/ext/sizeless-3.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-3.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,22 @@
+// { dg-options "-std=c++17" }
+
+__sizeless_struct sizeless_pair {
+  int a, b;
+};
+
+void
+f (sizeless_pair pair)
+{
+  { auto [i] = pair; } // { dg-error {only 1 name provided for structured binding} }
+  { auto [i, j] = pair; }
+  { auto [i, j, k] = pair; } // { dg-error {3 names provided for structured binding} }
+  { auto &[i, j] = pair; }
+}
+
+template<int N>
+int
+templated_f (sizeless_pair pair)
+{
+  auto [i, j] = pair;
+  return i + j + N;
+}
Index: gcc/testsuite/g++.dg/ext/sizeless-4.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-4.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,20 @@
+// { dg-do run }
+
+__sizeless_struct s
+{
+#if __cplusplus >= 201103L
+  int x = 12345;
+#else
+  s () : x (12345) {}
+  int x;
+#endif
+};
+
+int
+main ()
+{
+  s s1;
+  if (s1.x != 12345)
+    __builtin_abort ();
+  return 0;
+}
Index: gcc/testsuite/g++.dg/ext/sizeless-5.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-5.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,12 @@
+// { dg-do run }
+
+struct t {
+  ~t () { __builtin_exit (0); }
+};
+
+__sizeless_struct s {
+  ~s () {}
+  t t1;
+};
+
+int main () { s (); __builtin_abort (); }
Index: gcc/testsuite/g++.dg/ext/sizeless-6.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-6.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,14 @@
+// { dg-options "-fdump-tree-gimple" }
+
+__sizeless_struct s
+{
+  int x;
+};
+
+__INTPTR_TYPE__
+foo ()
+{
+  return (__INTPTR_TYPE__) &((s *) 0)->x;
+}
+
+// { dg-final { scan-tree-dump-not {->x} "gimple" } }
Index: gcc/testsuite/g++.dg/ext/sizeless-7.C
===================================================================
--- /dev/null	2018-09-14 11:16:31.122530289 +0100
+++ gcc/testsuite/g++.dg/ext/sizeless-7.C	2018-10-15 14:13:37.868189791 +0100
@@ -0,0 +1,20 @@
+// { dg-run }
+
+__sizeless_struct s
+{
+  s () { x = 0; }
+  void f () { x = 1000; }
+  int x;
+};
+
+void (s::*f_ptr) (void) = &s::f;
+
+int
+main ()
+{
+  s s1;
+  (s1.*f_ptr) ();
+  if (s1.x != 1000)
+    __builtin_abort ();
+  return 0;
+}


[-- Attachment #2: sizeless-types-in-c++.tar.gz --]
[-- Type: application/gzip, Size: 52923 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (9 preceding siblings ...)
  2018-10-15 15:01 ` [10/10] C++ " Richard Sandiford
@ 2018-10-15 15:14 ` Joseph Myers
  2018-10-15 18:57 ` Uecker, Martin
  11 siblings, 0 replies; 31+ messages in thread
From: Joseph Myers @ 2018-10-15 15:14 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: gcc-patches, jason, nathan, nd

On Mon, 15 Oct 2018, Richard Sandiford wrote:

> The patches therefore add a new "__sizeless_struct" keyword to denote
> structures that are sizeless rather than sized.  Unlike normal
> structures, these structures can have members of sizeless type in
> addition to members of sized type.  On the other hand, they have all
> the same limitations as other sizeless types (described in earlier
> sections).

I don't see anything here disallowing offsetof on such structures.

> Edits to the C standard
> =======================
> 
> This section specifies the behaviour for sizeless types as an edit to N1570.

That's a very old standard version.

I'm not in Pittsburgh this week, but I don't see anything to do with these 
ideas on the agenda.  I haven't seen any contributions from Arm to the 
ongoing discussions on the WG14 reflector that include issues relating to 
possibly runtime sized types (vectors, bignums, types representing 
information about another type, for example), unless they're using 
not-obviously-Arm email addresses.  Is Arm going to be engaging in those 
discussions and working with people interested in these areas to produce 
proposals that take account of the different ideas people have for use of 
non-VLA types that may not have a compile-time-constant size (some of 
which may not end up in the C standard, of course)?  (It might of course 
require multiple papers, e.g. starting with fixed-width vector types which 
as a widely-implemented feature are something it might be natural to 
consider for C2x.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
                   ` (10 preceding siblings ...)
  2018-10-15 15:14 ` [00/10][RFC] Splitting the C and C++ concept of "complete type" Joseph Myers
@ 2018-10-15 18:57 ` Uecker, Martin
  2018-10-16  8:51   ` Richard Biener
  2018-10-16 12:55   ` Richard Sandiford
  11 siblings, 2 replies; 31+ messages in thread
From: Uecker, Martin @ 2018-10-15 18:57 UTC (permalink / raw)
  To: gcc-patches, richard.sandiford; +Cc: jason, nd, nathan, joseph


Hi Richard,

as Joseph pointed out, there are some related discussions
on the WG14 reflector. How a about moving the discussion
there?

I find your approach very interesting and that it already
comes with an implementation is of course very useful

But I don't really understand the reasons why this is not based
on (2). These types are not "sizeless" at all, their size
just isn't known at compile time. So to me this seems to me
a misnomer.

In fact, to me these types *do* in fact seem very similar
to VLAs as VLAs are also complete types which also do no
have a known size at compile time.

That arrays decay to pointers doesn't mean that we
couldn't have similar vectors types which don't decay.
This is hardly a fundamental problem.

I also don't understand the problem about the array
size. If I understand this correctly, the size is somehow
known at run-time and implicitly passed along with the
values. So these new types do not need to have a
size expression (as in your proposal). 

Assignment, the possibility to return the type from
functions, and something like __sizeless_structs would
make sense for VLAs too.

So creating a new category "variable-length types" for 
both VLAs and variably-length vector types seems do make
much more sense to me. As I see it, this would be mainly
a change in terminology and not so much of the underlying
approach.


Best,
Martin

Am Montag, den 15.10.2018, 15:30 +0100 schrieb Richard Sandiford:
> The C standard says:
> 
>     At various points within a translation unit an object type may be
>     "incomplete" (lacking sufficient information to determine the size of
>     objects of that type) or "complete" (having sufficient information).
> 
> For AArch64 SVE, we'd like to split this into two concepts:
> 
>   * has the type been fully defined?
>   * would fully-defining the type determine its size?
> 
> This is because we'd like to be able to represent SVE vectors as C and C++
> types.  Since SVE is a "vector-length agnostic" architecture, the size
> of the vectors is determined by the runtime environment rather than the
> programmer or compiler.  In that sense, defining an SVE vector type does
> not determine its size.  It's nevertheless possible to use SVE vector types
> in meaningful ways, such as having automatic vector variables and passing
> vectors between functions.
> 
> The main questions in the RFC are:
> 
>   1) is splitting the definition like this OK in principle?
>   2) are the specific rules described below OK?
>   3) coding-wise, how should the split be represented in GCC?
> 
> Terminology
> -----------
> 
> Going back to the second bullet above:
> 
>   * would fully-defining the type determine its size?
> 
> the rest of the RFC calls a type "sized" if fully defining it would
> determine its size.  The type is "sizeless" otherwise.
> 
> Contents
> --------
> 
> The RFC is organised as follows.  I've erred on the side of including
> detail rather than leaving it out, but each section is meant to be
> self-contained and skippable:
> 
>   - An earlier RFC
>   - Quick overview of SVE
>   - Why we need SVE types in C and C++
>   - How we ended up with this definition
>   - The SVE types in more detail
>   - Outline of the type system changes
>   - Sizeless structures (and testing on non-SVE targets)
>   - Other variable-length vector architectures
>   - Edits to the C standard
>     - Base changes
>     - Updates for consistency
>     - Sizeless structures
>   - Edits to the C++ standard
>   - GCC implementation questions
> 
> I'll follow up with patches that implement the split.
> 
> 
> 
> An earlier RFC
> ==============
> 
> For the record (in case this sounds familiar) I sent an RFC about the
> sizeless type extension a while ago:
> 
>     https://gcc.gnu.org/ml/gcc/2017-08/msg00012.html
> 
> The rules haven't changed since then, but this version includes more
> information and includes support for sizeless structures.
> 
> 
> Quick overview of SVE
> =====================
> 
> SVE is a vector extension to AArch64.  A detailed description is
> available here:
> 
>     https://static.docs.arm.com/ddi0584/a/DDI0584A_a_SVE_supp_armv8A.pdf
> 
> but the only feature that really matters for this RFC is that SVE has no
> fixed or preferred vector length.  Implementations can instead choose
> from a range of possible vector lengths, with 128 bits being the minimum
> and 2048 bits being the maximum.  Priveleged software can further
> constrain the vector length within the range offered by the implementation;
> e.g. linux currently provides per-thread control of the vector length.
> 
> 
> Why we need SVE types in C and C++
> ==================================
> 
> SVE was designed to be an easy target for autovectorising normal scalar
> code.  There are also various language extensions that support explicit
> data parallelism or that make explicit vector chunking easier to do in
> an architecture-neutral way (e.g. C++ P0214).  This means that many users
> won't need to do anything SVE-specific.
> 
> Even so, there's always going to be a place for writing SVE-specific
> optimisations, with full access to the underlying ISA.  As for other
> vector architectures, we'd like users to be able to write such routines
> in C and C++ rather than force them to go all the way to assembly.
> 
> We'd also like C and C++ functions to be able to take SVE vector
> parameters and return SVE vector results, which is particularly useful
> when implementing things like vector math routines.  In this case in
> particular, the types need to map directly to something that fits in
> an SVE register, so that passing and returning vectors has minimal
> overhead.
> 
> 
> How we ended up with this definition
> ====================================
> 
> Requirements
> ------------
> 
> We need the SVE vector types to define and use SVE intrinsic functions
> and to write SVE vector library routines.  The key requirements when
> defining the types were:
> 
>   * They must be available in both C and C++ (because we want to be able
>     add SVE optimisations to C-only codebases).
> 
>   * They must fit in an SVE vector register (so there can be no on-the-side
>     information).
> 
>   * 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's what intrinsics and vector library routines need to do).
> 
>   * It must be possible to use the types in _Generic associations
>     (so that _Generic can be used to provide tgmath.h-style overloads).
> 
>   * It must be possible to use 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++).
> 
> Ideally, there'd also be a way of grouping SVE vectors together into tuples,
> since the ISA has instructions like LD2 that return multiple vectors.
> It would be good if users could also define their own tuple types, on top
> of the ones needed by the intrinsics, although that's more "nice to have".
> 
> Possible approaches
> -------------------
> 
> The main complication is that the size of an SVE vector is not a
> compile-time constant.  It seems that any approach to handling this
> 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).
> 
> Why (2) seemed like a bad idea
> ------------------------------
> 
> (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 declarations of functions that
> pass or return variable-length types.
> 
> 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 (unseen) extensions, even those that are aware of
> variable-length arrays.  Also, AIUI, variable-length arrays were added
> to an early draft of C++14, but were later removed as too controversial
> and didn't 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.
> 
> Why (3) seemed like a bad idea
> ------------------------------
> 
> (3) can be divided into two:
> 
> (3a) The vector types have a constant size and are large enough for all
>      possible vector lengths.
> 
>     The main problem with this is that the maximum size of an SVE
>     vector (2048 bits) is much larger than the minimum size (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:
> 
> 	struct s { _Alignas(16) int i; };
> 
>     except that the amount of padding would only be known at runtime.
> 
>     There's also a significant conceptual problem: encoding a fixed size
>     goes against a guiding principle of SVE, in which there is no preferred
>     vector length.  There's 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.
> 
> (3b) The vector types have a constant size and refer to separate storage
>      (as for std::string etc.)
> 
>     This would be difficult to do without C++-style constructor, destructor,
>     copy and move semantics, so wouldn't work well in C.  And in C++ it would
>     be less efficient than the proposed approach, since presumably an Allocator
>     would be needed to allocate the separate storage.  It would also require
>     a complicated ABI mapping to ensure that the vectors can still be passed
>     and returned in registers.
> 
> Chosen approach
> ---------------
> 
> We therefore took approach (1) and classified C and C++ types as "sized"
> (having a measurable size when fully-defined) and "sizeless" (never
> having a measurable size).  Sizeless types have no defined size,
> alignment or layout at the language level, with those things becoming
> purely an ABI-level detail.
> 
> We then treated all sizeless types as permanently incomplete.
> On its own, this would put them in a similar situation to "void"
> (although they wouldn't be exactly the same, since there are some
> specific rules for "void" that don't apply to incomplete types in
> general).  We then relaxed specific rules until the types were actually
> useful.
> 
> Things in favour of (1)
> -----------------------
> 
> The reasons above were mostly negative, arriving at (1) by elimination.
> A more positive justification of this approach is that it 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 language extensions.  By default,
> any language extension would treat the new types like other incomplete
> types and choose conservatively-correct behaviour.  It would then be
> possible to relax the language extension if this default behaviour
> turns out to be too restrictive.
> 
> (That said, treating the types as permanently incomplete still won't
> 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.)
> 
> 
> The SVE types in more detail
> ============================
> 
> Arm has published an SVE "ACLE" that specifies the SVE types and intrinsic
> functions in detail.  For reference this is available without registration
> at:
> 
>     https://static.docs.arm.com/100987/0000/acle_sve_100987_0000_00_en.pdf
> 
> but I'll try to keep this self-contained.
> 
> The ACLE defines a vector type sv<base>_t for each supported element type
> <base>_t, so that the complete set is:
> 
>     svint8_t      svint16_t     svint32_t     svint64_t
>     svuint8_t     svuint16_t    svuint32_t    svuint64_t
>                   svfloat16_t   svfloat32_t   svfloat64_t
> 
> The types in each column have the same number of lanes and have twice
> as many lanes as those in the column to the right.  Every vector has
> the same number of bytes in total, with the number of bytes being
> determined at runtime.
> 
> The ACLE also defines a single predicate type:
> 
>     svbool_t
> 
> that has the same number of lanes as svint8_t and svuint8_t.
> 
> All these types are opaque builtin types and are only expected to
> be used with the associated ACLE intrinsics.  There are intrinsics for
> creating vectors from scalars, loading from scalars, storing to scalars,
> reinterpreting one type as another, etc.
> 
> The idea is that the vector types would only be used for short-term
> register-sized working data.  Longer-term data would typically be stored
> out to arrays.
> 
> For example, the vector function underlying:
> 
>     #pragma omp declare simd
>     double sin(double);
> 
> would be:
> 
>     svfloat64_t mangled_sin(svfloat64_t, svbool_t);
> 
> (The svbool_t is because SVE functions should be predicated by default,
> to avoid the need for a scalar epilogue loop.)
> 
> The ACLE also defines x2, x3 and x4 tuple types for each vector type;
> for example, svint8x3_t is a tuple of 3 svint8_ts.  The tuples are
> structure-like types with fields v0, v1, v2 and v3, up to the number
> required.
> 
> 
> Outline of the type system changes
> ==================================
> 
> Going back to the summary at the start of the RFC, C classifies types as
> "complete" (the size of objects can be calculated) or "incomplete" (the
> size of objects can't be calculated).  There's very little you can do
> with a type until it becomes complete.
> 
> The approach we took was to treat all the SVE types as permanently
> incomplete.  We then went through the standard relaxing specific
> rules until the types were actually useful.
> 
> The first step was to classify types as:
> 
>   * "indefinite" (lacking sufficient information to create an object of
>     that type) or "definite" (having sufficient information)
> 
>   * "sized" (will have a known size when definite) or "sizeless" (will
>     never have a known 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 unchanged from the standard.
> Thus a "definite type" is one that has been fully-defined rather than
> simply declared, and "complete" is now equivalent to "sized and definite".
> All standard types are "sized" (even "void", although it's always
> indefinite and incomplete).
> 
> We then needed to make some rules use the distinction between "indefinite"
> and "definite" rather than "incomplete" and "complete".  The specific
> things we wanted to allow were:
> 
>   * defining automatic variables with sizeless definite type
>   * defining functions whose parameters have sizeless definite type
>   * defining functions that return a sizeless definite type
>   * using sizeless definite types in _Generic associations
>   * dereferencing pointers to sizeless definite types
> 
> Specific things we wanted to remain invalid -- by inheriting the rules from
> incomplete types -- were:
> 
>   * creating or accessing arrays that have sizeless element types
>   * doing pointer arithmetic on pointers to sizeless types
>   * using sizeof and _Alignof with a sizeless type (or object of sizeless type)
>   * defining (sized) unions or structures with sizeless members
> 
> It also seemed worth adding an extra restriction:
> 
>   * variables with sizeless type must not have static or thread-local
>     storage duration
> 
> In practice it's impossible to define such variables with incomplete type,
> but having an explicit rule means that things like:
> 
>     extern svint8_t foo;  // An SVE vector of int8_t elements.
> 
> are outright invalid rather than simply useless (because no other
> translation unit could ever define foo).  Similarly, without an
> explicit rule:
> 
>     svint8_t foo;         // An SVE vector of int8_t elements.
> 
> 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.
> 
> This restriction isn't critical but it gives better diagnostics.
> 
> 
> Sizeless structures (and testing on non-SVE targets)
> ====================================================
> 
> We're planning to build all SVE intrinsic types directly into GCC
> (patches already written).  SVE therefore doesn't strictly need a syntax
> for creating new sizeless types in C and C++.  However, having a way of
> creating new structure-like "sizeless" types would be useful for three
> reasons:
> 
>   - Functions could return arbitrary data by value.  The SVE ABI allows
>     a function to return up to 8 vectors and 4 predicates in registers,
>     which is far more flexible than the intrinsic types.
> 
>   - We could use these sizeless structure types to test the functionality
>     on all targets.
> 
>   - A lot of the C++ frontend is concerned with classes, and having
>     a way of creating sizeless classes would help make the C++ changes
>     more consistent.
> 
> The patches therefore add a new "__sizeless_struct" keyword to denote
> structures that are sizeless rather than sized.  Unlike normal
> structures, these structures can have members of sizeless type in
> addition to members of sized type.  On the other hand, they have all
> the same limitations as other sizeless types (described in earlier
> sections).
> 
> E.g., a sizeless structure definition might look like:
> 
>     __sizeless_struct data {
>       double *array;
>       svuint64_t indices;  // An SVE vector of uint64_t elements.
>       svbool_t active;     // An SVE predicate.
>     };
> 
> Adding a new keyword seemed better than using an attribute because it
> means that the sized vs. sizeless distinction is fixed by the declaration.
> E.g.:
> 
>     struct data;                     // Is it sized or sizeless?
>     extern struct data global_data;  // OK if sized, not if sizeless.
>     struct __attribute__((sizeless)) data {
>       double *array;
>       svuint64_t indices;            // An SVE vector of uint64_t elements.
>       svbool_t active;               // An SVE predicate.
>     };
> 
> would lead to the declaration of "global_data" sneaking through
> despite being invalid when "data" is sizeless.
> 
> The tests in the patches all use these __sizeless_structs; they contain
> nothing SVE- or AArch64-specific.
> 
> 
> Other variable-length vector architectures
> ==========================================
> 
> The proposed RISC-V vector extension also has variable-length vectors.
> When this language change was discussed on the clang developers' list,
> Bruce Hoult (from SiFive, but speaking personally) replied with:
> 
>     http://lists.llvm.org/pipermail/cfe-dev/2018-May/057943.html
> 
> That message covers some of the background about the vector extension.
> On the language changes, Bruce said:
> 
>     > However, even though the length is variable, the concept of a
>     > "register-sized" C and C++ vector type makes just as much sense for SVE
>     > as it does for other vector architectures.  Vector library functions
>     > take such register-sized vectors as input and return them as results.
>     > Intrinsic functions are also just as useful as they are for other vector
>     > architectures, and they too take register-sized vectors as input and
>     > return them as results.
> 
>     Intrinsic functions are absolutely required, and are I think the main
>     reason for such a low-level register-sized vector type to exist.
> 
> [ Bruce went on to say:
> 
>     I'm not sure whether user-written functions operating on register-sized
>     vectors are useful enough to support. User-written functions would normally
>     take and return a higher-level vector type, and would implement the desired
>     functionality in terms of calls to other user-written functions (operating
>     on the high level vector as a whole) and/or explicit loops iterating
>     through the high level vector type using intrinsic functions on the
>     register-sized vector type proposed here.
> 
> But this use case is very important for SVE, since it will allow us
> to implement vector math routines in a way that works with the OpenMP
> "declare simd" construct.  There was also talk on gcc@ recently about
> supporting this style of interface for RISC-V. ]
> 
> [...]
> 
>     > All these types are opaque builtin types and are only intended to be
>     > used with the associated ACLE intrinsics.  There are intrinsics for
>     > creating vectors from scalars, loading from scalars, storing to scalars,
>     > reinterpreting one type as another, etc.
>     >
>     > The idea is that the vector types would only be used for short-term
>     > register-sized working data.  Longer-term data would typically be stored
>     > out to arrays.
> 
>     I agree with this.
> 
> [...]
> 
>     > The approach we took was to treat all the SVE types as permanently
>     > incomplete.
> 
>     This seems reasonable.
> 
> So it looks like this extension would be useful for at least one
> architecture besides SVE.
> 
> 
> Edits to the C standard
> =======================
> 
> This section specifies the behaviour for sizeless types as an edit to N1570.
> There are three stages:
> 
>   - base changes, which add enough support for built-in sizeless
>     vector types
> 
>   - updates for consistency, which change some of the wording without
>     changing the meaning
> 
>   - support for sizeless structures
> 
> In each case, -strikethrough- indicates deleted text and *bold*
> includes additional text.
> 
> 
> Base changes
> ------------
> 
> These changes are enough to support sizeless built-in vector types.
> 
>     6.2.5 Types
>     -----------
> 
>     1. The meaning of a value stored in an object or returned by a
>     function is determined by the type of the expression used to access
>     it. … Types are partitioned into object types (types that
>     describe objects) and function types (types that describe
>     functions).  -At various points within a translation unit an object
>     type may be incomplete (lacking sufficient information to determine
>     the size of objects of that type) or complete (having sufficient
>     information).37)- *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.*
> 
>     1A. *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).37) 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.*
> 
>     1B. *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.*
> 
>     …
> 
>     19. The void type comprises an empty set of values; it is -an
>     incomplete- *a sized indefinite* object type that cannot be completed
>     *(made definite)*.
> 
>     …
> 
>     37) A type may be -incomplete- *indefinite* or -complete- *definite*
>     throughout an entire translation unit, or it may change states at
>     different points within a translation unit.
> 
>     …
> 
>     6.3.2.1 Lvalues, arrays, and function designators
>     -------------------------------------------------
> 
>     1.  An lvalue is an expression (with an object type other than void)
>     that potentially designates an object;64) … A modifiable lvalue is
>     an lvalue that does not have array type, does not have an
>     -incomplete- *indefinite* type, does not have a const-qualified
>     type, …
> 
>     2.  Except when it is the operand of the sizeof operator, the
>     _Alignof operator, the unary & operator, the ++ operator, the --
>     operator, or the left operand of the . operator or an assignment
>     operator, an lvalue that does not have array type is converted to
>     the value stored in the designated object (and is no longer an
>     lvalue); this is called lvalue conversion. … If the lvalue has an
>     -incomplete- *indefinite* type and does not have array type, the
>     behavior is undefined. …
> 
>     …
> 
>     6.5.1.1 Generic selection
>     -------------------------
> 
>     …
> 
>     Constraints
> 
>     2. A generic selection shall have no more than one default generic
>     association. The type name in a generic association shall specify a
>     -complete- *definite* object type other than a variably modified
>     type. …
> 
>     …
> 
>     6.5.2.2 Function calls
>     ----------------------
> 
>     Constraints
> 
>     1. The expression that denotes the called function92) shall have
>     type pointer to function returning void or returning a -complete-
>     *definite* object type other than an array type.
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     4. An argument may be an expression of any -complete- *definite* object
>     type. …
> 
>     …
> 
>     6.5.2.5 Compound literals
>     -------------------------
> 
>     Constraints
> 
>     1. The type name shall specify a -complete- *definite* object type or an
>     array of unknown size, but not a variable length array type.
> 
>     …
> 
>     6.7 Declarations
>     ----------------
> 
>     Constraints
> 
>     …
> 
>     4A. *If an identifier for an object does not have automatic storage
>     duration, its type must be sized rather than sizeless.*
> 
>     Semantics
> 
>     …
> 
>     7. If an identifier for an object is declared with no linkage, the
>     type for the object shall be -complete- *definite* by the end of its
>     declarator, or by the end of its init-declarator if it has an
>     initializer; in the case of function parameters (including in
>     prototypes), it is the adjusted type (see 6.7.6.3) that is required
>     to be -complete- *definite*.
> 
>     …
>      
>     6.7.6.3 Function declarators (including prototypes) 
>     ---------------------------------------------------
> 
>     Constraints
> 
>     …
> 
>     4. After adjustment, the parameters in a parameter type list in a
>     function declarator that is part of a definition of that function
>     shall not have -incomplete- *indefinite* type.
> 
>     …
> 
>     6.7.9 Initialization
>     --------------------
> 
>     Constraints
> 
>     …
> 
>     3. The type of the entity to be initialized shall be an array of
>     unknown size or a -complete- *definite* object type that is not a
>     variable length array type.
> 
>     …
> 
>     6.9.1 Function definitions
>     --------------------------
> 
>     Constraints
> 
>     …
> 
>     3. The return type of a function shall be void or a -complete-
>     *definite* object type other than array type.
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     7. The declarator in a function definition specifies the name of the
>     function being defined and the identifiers of its parameters. …
>     [T]he type of each parameter is adjusted as described in
>     6.7.6.3 for a parameter type list; the resulting type shall be a
>     -complete- *definite* object type.
> 
>     …
> 
>     J.2 Undefined behavior
>     ----------------------
> 
>         …
>       * A non-array lvalue with -an incomplete- *an indefinite* type is used
>         in a context that requires the value of the designated object
>         (6.3.2.1).
>         …
>       * An identifier for an object is declared with no linkage and the
>         type of the object is -incomplete- *indefinite* after its
>         declarator, or after its init-declarator if it has an
>         initializer (6.7).
>         …
>       * An adjusted parameter type in a function definition is not a
>         -complete- *definite* object type (6.9.1).
>         …
> 
> Updates for consistency
> -----------------------
> 
> These changes are a prerequisite for sizeless structures.  They have no
> effect otherwise, but might be preferred anyway because they make the
> terminology more consistent.  They apply on top of the previous edits.
> 
>     6.2.5 Types
>     -----------
> 
>     …
> 
>     22. An array type of unknown size is an -incomplete- *indefinite*
>     type. It is -completed- *made definite*, for an identifier of that type,
>     by specifying the size in a later declaration (with internal or
>     external linkage). A structure or union type of unknown content (as
>     described in 6.7.2.3) is an -incomplete- *indefinite* type. It is
>     -completed- *made definite*, for all declarations of that type, by
>     declaring the same structure or union tag with its defining content
>     later in the same scope.
> 
>     …
> 
>     6.2.7 Compatible type and composite type
>     ----------------------------------------
> 
>     1. Two types have compatible type if their types are the same. …
>     Moreover, two structure, union, or enumerated types declared in
>     separate translation units are compatible if their tags and members
>     satisfy the following requirements: If one is declared with a tag,
>     the other shall be declared with the same tag. If both are
>     -completed- *made definite* anywhere within their respective
>     translation units, then the following additional requirements apply: …
> 
>     …
> 
>     6.7.2.1 Structure and union specifiers
>     --------------------------------------
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     8. The presence of a struct-declaration-list in a
>     struct-or-union-specifier declares a new type, within a translation
>     unit. The struct-declaration-list is a sequence of declarations for
>     the members of the structure or union.  If the struct-declaration-list
>     does not contain any named members, either directly or via an anonymous
>     structure or anonymous union, the behavior is undefined.  The type is
>     -incomplete- *indefinite* until immediately after the } that terminates
>     the list, and -complete- *definite* thereafter.
> 
>     …
> 
>     6.7.2.2 Enumeration specifiers
>     ------------------------------
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     4. … The enumerated type is -incomplete- *indefinite* until
>     immediately after the } that terminates the list of enumerator
>     declarations, and -complete- *definite* thereafter.
> 
>     …
> 
>     6.7.2.3 Tags
>     ------------
> 
>     …
> 
>     Semantics
> 
>     4. All declarations of structure, union, or enumerated types that
>     have the same scope and use the same tag declare the same
>     type. Irrespective of whether there is a tag or what other
>     declarations of the type are in the same translation unit, the type
>     is -incomplete- *indefinite* 129) until immediately after the closing
>     brace of the list defining the content, and -complete- *definite*
>     thereafter.
> 
>     …
> 
>     8. If a type specifier of the form
> 
>     struct-or-union identifier
> 
>     occurs other than as part of one of the above forms, and no other
>     declaration of the identifier as a tag is visible, then it declares
>     an -incomplete- *indefinite* structure or union type, and declares the
>     identifier as the tag of that type.131)
> 
>     …
> 
>     129) An -incomplete- *indefinite* type may only by used when -the
>     size of an object- *the ability to create an object* of that type
>     is not needed.  It is not needed, for example, when a typedef name
>     is declared to be a specifier for a structure or union, or when a
>     pointer to or a function returning a structure or union is being
>     declared. (See -incomplete- *indefinite* types in 6.2.5.) The
>     specification has to be -complete- *definite* before such a function
>     is called or defined.
> 
>     6.7.6.3 Function declarators (including prototypes) 
>     ---------------------------------------------------
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     12.  If the function declarator is not part of a definition of that
>     function, parameters may have -incomplete- *indefinite* type and may use
>     the [*] notation in their sequences of declarator specifiers to
>     specify variable length array types.
> 
>     …
> 
>     J.2 Undefined behavior
>     ----------------------
> 
>         …
>       * When the -complete- *definite* type is needed, an -incomplete-
>         *indefinite* structure or union type is not completed in the same
>         scope by another declaration of the tag that defines the content
>         (6.7.2.3).
>         …
> 
> Sizeless structures
> -------------------
> 
> These additional changes to N1570 add the concept of a sizeless structure.
> Again they apply on top of the edits above:
> 
>     6.2.3 Name spaces of identifiers
>     --------------------------------
> 
>     1. If more than one declaration of a particular identifier is
>     visible at any point in a translation unit, the syntactic context
>     disambiguates uses that refer to different entities. Thus, there
>     are separate name spaces for various categories of identifiers, as
>     follows:
> 
> 	…
> 
>       * the tags of *sized* structures, *sizeless structures,* unions, and
> 	enumerations (disambiguated by following any32) of the keywords
> 	struct, *__sizeless_struct,* union, or enum);
> 
> 	…
> 
>     6.2.5 Types
>     -----------
> 
>     1. … Types are partitioned into object types (types that describe
>     objects) and function types (types that describe functions).
>     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.-
>     *the only sizeless types defined by this standard are __sizeless_structs,
>     but an implementation may provide additional sizeless types.*
> 
>     …
> 
>     1B. 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.
> 
>     …
> 
>     20. Any number of derived types can be constructed from the object
>     and function types, as follows: …
> 
>       * A *sized* structure type describes a sequentially allocated
>         nonempty set of sized member objects (and, in certain
>         circumstances, an incomplete array), each of which has an
>         optionally specified name and possibly distinct type.
> 
>       * *A sizeless structure type describes a set of non-overlapping
>         member objects whose types may be sizeless and whose relative
>         positions are unspecified.  It is also unspecified whether the
>         structure occupies a single contiguous piece of storage or
>         whether it requires several disjoint pieces.*
> 
>     …
> 
>     *20A. The term structure type refers collectively to sized structure
>     types and sizeless structure types.*
> 
>     …
> 
>     6.4.1 Keywords
>     --------------
> 
>     Syntax
> 
>     1. *(Add __sizeless_struct to the list and update the copy in A.1.2)*
> 
>     …
> 
>     6.5.8 Relational operators
>     --------------------------
> 
>     …
> 
>     Semantics
> 
>     …
> 
>     5. When two pointers are compared, the result depends on the
>     relative locations in the address space of the objects pointed to.
>     … If the objects pointed to are members of the same aggregate object,
>     pointers to *sized* structure members declared later compare greater
>     than pointers to members declared earlier in the structure, and
>     pointers to array elements with larger subscript values compare
>     greater than pointers to elements of the same array with lower
>     subscript values. …
> 
>     …
> 
>     6.7.2.1 Structure and union specifiers
>     --------------------------------------
> 
>     Syntax
> 
>     struct-or-union-specifier:
>         struct-or-union identifieropt { struct-declaration-list }
>         struct-or-union identifier
> 
>     struct-or-union:
>         struct
>         *__sizeless_struct*
>         union
> 
>     …
> 
>     3. A *sized* structure or union shall not contain a member with
>     incomplete or function type …, except that the last member of a
>     structure with more than one named member may have incomplete array
>     type; such a structure (and any union containing, possibly
>     recursively, a member that is such a structure) shall not be a
>     member of a structure or an element of an array.  *Simlarly, a
>     sizeless structure shall not contain a member with indefinite or
>     function type; the exception for incomplete array types does not
>     apply.*
> 
>     …
> 
>     Semantics
> 
>     6. As discussed in 6.2.5, a *sized* structure is a type consisting
>     of a sequence of members, whose storage is allocated in an ordered
>     sequence; *a sizeless structure is a type consisting of
>     non-overlapping members whose relative position is unspecified,*
>     and a union is a type consisting of a sequence of members whose
>     storage overlap.
> 
>     7. Structure and union specifiers have the same form. The keywords
>     struct, *__sizeless_struct* and union indicate that the type being
>     specified is, respectively, a *sized* structure type, *a sizeless
>     structure type,* or a union type.
> 
>     …[8 is as above]…
> 
>     9. A member of a structure or union may have any complete object
>     type other than a variably modified type.123)  *A member of a sizeless
>     structure may also have a sizeless definite type.*  In addition, a
>     member *of a structure or union* may be declared to consist of a
>     specified number of bits (including a sign bit, if any). Such a
>     member is called a bit-field;124) its width is preceded by a colon.
> 
>     …
> 
>     15. Within a *sized* structure object, the non-bit-field members and
>     the units in which bit-fields reside have addresses that increase in
>     the order in which they are declared. A pointer to a *sized* structure
>     object, suitably converted, points to its initial member (or if that
>     member is a bit-field, then to the unit in which it resides), and
>     vice versa. There may be unnamed padding within a *sized* structure
>     object, but not at its beginning.
> 
>     15A. *The representation of a sizeless structure object is
>     unspecified.  It is possible to form pointers to the structure
>     itself and to its individual members, but the relationship between
>     their addresses is unspecified.  The structure may occupy a single
>     piece of contiguous storage or it may occupy several disjoint
>     pieces.*
> 
>     …
> 
>     18 As a special case, the last element of a *sized* structure with
>     more than one named member may have an incomplete array type; this
>     is called a flexible array member. …
> 
>     …
> 
>     6.7.2.3 Tags
>     ------------
> 
>     Constraints
> 
>     …
> 
>     2. Where two declarations that use the same tag declare the same
>     type, they shall both use the same choice of struct, *__sizeless_struct,*
>     union, or enum.
> 
>     …
> 
> 
> Edits to the C++ standard
> =========================
> 
> We have a similar set of changes to the C++ standard, but this RFC is
> long enough already, so I've not included them here.  I also didn't find
> them to be particularly useful when writing the C++ patches, since most
> of the changes were obvious given a few basic rules.  Those rules are:
> 
>   - type traits can be used with sizeless types (unlike incomplete types)
> 
>   - sizeless structures cannot have base classes or be used as base classes
> 
>   - sizeless structures cannot have virtual members
> 
>   - pointers to member variables are invalid for sizeless structures
>     (although taking the address of a member of a specific sizeless object
>     is fine, as for C)
> 
>   - sizeless types are not literal types
> 
>   - sizeless types cannot be created by operator new (as for incomplete types)
> 
>   - sizeless types cannot be deleted (so, unlike for incomplete types,
>     this is an error rather than a warning)
> 
>   - sizeless types cannot be thrown or caught (as for incomplete types)
> 
>   - sizeless types cannot be used with typeid() (as for incomplete types)
> 
> 
> GCC implementation questions
> ============================
> 
> The GCC patches are pretty simple in principle.  The language changes
> involve going through the standard replacing "complete" with "definite"
> and most of the GCC patches go through the frontend code making the
> same kind of change.
> 
> New type flag for sizeless types
> --------------------------------
> 
> The patches add a new flag TYPE_SIZELESS_P to represent the negative of:
> 
>   * would fully-defining the type determine its size?
> 
> from the summary above.  Negative names are usually a bad thing,
> but the natural default is for the flag to be off.
> 
> There are currently 17 bits free in tree_type_common, so the patches
> steal one of those.  Is that OK?
> 
> The effect on COMPLETE_TYPE_P
> -----------------------------
> 
> The current definition of COMPLETE_TYPE_P is:
> 
>     /* Nonzero if this type is a complete type.  */
>     #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
> 
> Although the SVE types don't have a measurable size at the language
> level, they still have a TYPE_SIZE and TYPE_SIZE_UNIT, with the sizes
> using placeholders for the runtime vector size.  So after the split
> described in the summary, TYPE_SIZE (NODE) != NULL_TREE means
> "the type is fully defined" rather than "the type is complete".
> With TYPE_SIZELESS_P, the definition of "complete type" would be:
> 
>     #define COMPLETE_TYPE_P(NODE) \
>       (TYPE_SIZE (NODE) != NULL_TREE && !TYPE_SIZELESS_P (NODE))
> 
> i.e. the type is fully-defined, and fully-defining it determines
> its size at the language level.
> 
> Uses of COMPLETE_TYPE_P outside the frontends
> ---------------------------------------------
> 
> The main complication is that the concept of "complete type" is exposed
> outside the frontends, with COMPLETE_TYPE_P being defined in tree.h.
> 
> I tried to audit all uses outside the frontends and it looks like
> they're all testing whether "the type is fully defined" and don't
> care about the distinction between sized and sizeless.  This means
> that the current definition (rather than the new definition)
> should be correct in all cases.
> 
> In some cases the tests are simple null checks, like:
> 
>      /* Try to approach equal type sizes.  */
>      if (!COMPLETE_TYPE_P (type_a)
>          || !COMPLETE_TYPE_P (type_b)
>          || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_a))
>          || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_b)))
>        break;
> 
> IMO it's more obvious to test TYPE_SIZE_UNIT directly for null here.
> Having a wrapper doesn't add much.
> 
> In places like:
> 
>   if (!COMPLETE_TYPE_P (t))
>     layout_type (t);
> 
> and:
> 
>   if (COMPLETE_TYPE_P (t) && TYPE_CANONICAL (t)
>       && TYPE_MODE (t) != TYPE_MODE (TYPE_CANONICAL (t)))
>     ...
> 
> it's testing whether the type has been laid out already.
> 
> So the patches do two things:
> 
>   * Expand the definition of the current COMPLETE_TYPE_P macro outside
>     the frontends if the macro is simply protecting against a null
>     dereference.
> 
>   * Make COMPLETE_TYPE_P local to the frontends and rename all uses
>     outside the frontends.
> 
> As far as the second point goes, I wasn't sure what new name to use
> outside the front ends.  Possibilities include:
> 
>   - DEFINITE_TYPE_P
>   - USABLE_TYPE_P
>   - VALID_VAR_TYPE_P
>   - TYPE_LAID_OUT_P
>   - TYPE_DEFINED_P
>   - TYPE_FULLY_DEFINED_P
>   - TYPE_READY_P
>   ...other suggestions welcome...
> 
> I went for DEFINITE_TYPE_P because that's what the SVE specification
> uses, but something more neutral like TYPE_DEFINED_P might be better.
> 
> Frontend changes
> ----------------
> 
> The frontend patches change COMPLETE_TYPE_P to DEFINITE_TYPE_P where
> necessary.  I've tried where possible to accompany each individual
> change with a test.
> 
> This worked fairly naturally (IMO) for C, and most of the changes could
> be tied directly to the language edits above.
> 
> For C++ it was more difficult (not surprisingly).  There are a lot of
> tests for COMPLETE_TYPE_P that are obviously testing whether a class
> has been fully defined, and are more concerned with name lookup than
> TYPE_SIZE.  The same goes for COMPLETE_OR_OPEN_TYPE_P and whether the
> definition has been started.  So while the C changes were relatively
> small and self-contained, the C++ changes replace many more uses of
> COMPLETE_TYPE_P than they keep.  This makes me wonder whether it's a
> good idea to keep COMPLETE_TYPE_P at all, or whether it would be better
> to replace the remaining uses with something more explicit like:
> 
>   TYPE_SIZE_KNOWN_P
>   TYPE_SIZE_DEFINED_P
>   TYPE_SIZE_MEASURABLE_P
>   TYPE_SIZE_COMPLETE_P
>   ...suggestions again welcome...
> 
> Thanks,
> Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-15 18:57 ` Uecker, Martin
@ 2018-10-16  8:51   ` Richard Biener
  2018-10-16 12:55   ` Richard Sandiford
  1 sibling, 0 replies; 31+ messages in thread
From: Richard Biener @ 2018-10-16  8:51 UTC (permalink / raw)
  To: Martin.Uecker
  Cc: GCC Patches, Richard Sandiford, Jason Merrill, nd,
	Nathan Sidwell, Joseph S. Myers

On Mon, Oct 15, 2018 at 8:40 PM Uecker, Martin
<Martin.Uecker@med.uni-goettingen.de> wrote:
>
>
> Hi Richard,
>
> as Joseph pointed out, there are some related discussions
> on the WG14 reflector. How a about moving the discussion
> there?
>
> I find your approach very interesting and that it already
> comes with an implementation is of course very useful
>
> But I don't really understand the reasons why this is not based
> on (2). These types are not "sizeless" at all, their size
> just isn't known at compile time. So to me this seems to me
> a misnomer.
>
> In fact, to me these types *do* in fact seem very similar
> to VLAs as VLAs are also complete types which also do no
> have a known size at compile time.
>
> That arrays decay to pointers doesn't mean that we
> couldn't have similar vectors types which don't decay.
> This is hardly a fundamental problem.
>
> I also don't understand the problem about the array
> size. If I understand this correctly, the size is somehow
> known at run-time and implicitly passed along with the
> values. So these new types do not need to have a
> size expression (as in your proposal).
>
> Assignment, the possibility to return the type from
> functions, and something like __sizeless_structs would
> make sense for VLAs too.
>
> So creating a new category "variable-length types" for
> both VLAs and variably-length vector types seems do make
> much more sense to me. As I see it, this would be mainly
> a change in terminology and not so much of the underlying
> approach.

I agree - those types very much feel like VLAs.

I think there's also the existing vector extension of GCC to
factor in which introduces (non-VLA) vector types to the
language and allows arithmetic on them as well as
indexing and passing and returing them by values.
Variable-size vectors should play well in that context.

Richard.

>
> Best,
> Martin
>
> Am Montag, den 15.10.2018, 15:30 +0100 schrieb Richard Sandiford:
> > The C standard says:
> >
> >     At various points within a translation unit an object type may be
> >     "incomplete" (lacking sufficient information to determine the size of
> >     objects of that type) or "complete" (having sufficient information).
> >
> > For AArch64 SVE, we'd like to split this into two concepts:
> >
> >   * has the type been fully defined?
> >   * would fully-defining the type determine its size?
> >
> > This is because we'd like to be able to represent SVE vectors as C and C++
> > types.  Since SVE is a "vector-length agnostic" architecture, the size
> > of the vectors is determined by the runtime environment rather than the
> > programmer or compiler.  In that sense, defining an SVE vector type does
> > not determine its size.  It's nevertheless possible to use SVE vector types
> > in meaningful ways, such as having automatic vector variables and passing
> > vectors between functions.
> >
> > The main questions in the RFC are:
> >
> >   1) is splitting the definition like this OK in principle?
> >   2) are the specific rules described below OK?
> >   3) coding-wise, how should the split be represented in GCC?
> >
> > Terminology
> > -----------
> >
> > Going back to the second bullet above:
> >
> >   * would fully-defining the type determine its size?
> >
> > the rest of the RFC calls a type "sized" if fully defining it would
> > determine its size.  The type is "sizeless" otherwise.
> >
> > Contents
> > --------
> >
> > The RFC is organised as follows.  I've erred on the side of including
> > detail rather than leaving it out, but each section is meant to be
> > self-contained and skippable:
> >
> >   - An earlier RFC
> >   - Quick overview of SVE
> >   - Why we need SVE types in C and C++
> >   - How we ended up with this definition
> >   - The SVE types in more detail
> >   - Outline of the type system changes
> >   - Sizeless structures (and testing on non-SVE targets)
> >   - Other variable-length vector architectures
> >   - Edits to the C standard
> >     - Base changes
> >     - Updates for consistency
> >     - Sizeless structures
> >   - Edits to the C++ standard
> >   - GCC implementation questions
> >
> > I'll follow up with patches that implement the split.
> >
> >
> >
> > An earlier RFC
> > ==============
> >
> > For the record (in case this sounds familiar) I sent an RFC about the
> > sizeless type extension a while ago:
> >
> >     https://gcc.gnu.org/ml/gcc/2017-08/msg00012.html
> >
> > The rules haven't changed since then, but this version includes more
> > information and includes support for sizeless structures.
> >
> >
> > Quick overview of SVE
> > =====================
> >
> > SVE is a vector extension to AArch64.  A detailed description is
> > available here:
> >
> >     https://static.docs.arm.com/ddi0584/a/DDI0584A_a_SVE_supp_armv8A.pdf
> >
> > but the only feature that really matters for this RFC is that SVE has no
> > fixed or preferred vector length.  Implementations can instead choose
> > from a range of possible vector lengths, with 128 bits being the minimum
> > and 2048 bits being the maximum.  Priveleged software can further
> > constrain the vector length within the range offered by the implementation;
> > e.g. linux currently provides per-thread control of the vector length.
> >
> >
> > Why we need SVE types in C and C++
> > ==================================
> >
> > SVE was designed to be an easy target for autovectorising normal scalar
> > code.  There are also various language extensions that support explicit
> > data parallelism or that make explicit vector chunking easier to do in
> > an architecture-neutral way (e.g. C++ P0214).  This means that many users
> > won't need to do anything SVE-specific.
> >
> > Even so, there's always going to be a place for writing SVE-specific
> > optimisations, with full access to the underlying ISA.  As for other
> > vector architectures, we'd like users to be able to write such routines
> > in C and C++ rather than force them to go all the way to assembly.
> >
> > We'd also like C and C++ functions to be able to take SVE vector
> > parameters and return SVE vector results, which is particularly useful
> > when implementing things like vector math routines.  In this case in
> > particular, the types need to map directly to something that fits in
> > an SVE register, so that passing and returning vectors has minimal
> > overhead.
> >
> >
> > How we ended up with this definition
> > ====================================
> >
> > Requirements
> > ------------
> >
> > We need the SVE vector types to define and use SVE intrinsic functions
> > and to write SVE vector library routines.  The key requirements when
> > defining the types were:
> >
> >   * They must be available in both C and C++ (because we want to be able
> >     add SVE optimisations to C-only codebases).
> >
> >   * They must fit in an SVE vector register (so there can be no on-the-side
> >     information).
> >
> >   * 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's what intrinsics and vector library routines need to do).
> >
> >   * It must be possible to use the types in _Generic associations
> >     (so that _Generic can be used to provide tgmath.h-style overloads).
> >
> >   * It must be possible to use 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++).
> >
> > Ideally, there'd also be a way of grouping SVE vectors together into tuples,
> > since the ISA has instructions like LD2 that return multiple vectors.
> > It would be good if users could also define their own tuple types, on top
> > of the ones needed by the intrinsics, although that's more "nice to have".
> >
> > Possible approaches
> > -------------------
> >
> > The main complication is that the size of an SVE vector is not a
> > compile-time constant.  It seems that any approach to handling this
> > 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).
> >
> > Why (2) seemed like a bad idea
> > ------------------------------
> >
> > (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 declarations of functions that
> > pass or return variable-length types.
> >
> > 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 (unseen) extensions, even those that are aware of
> > variable-length arrays.  Also, AIUI, variable-length arrays were added
> > to an early draft of C++14, but were later removed as too controversial
> > and didn't 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.
> >
> > Why (3) seemed like a bad idea
> > ------------------------------
> >
> > (3) can be divided into two:
> >
> > (3a) The vector types have a constant size and are large enough for all
> >      possible vector lengths.
> >
> >     The main problem with this is that the maximum size of an SVE
> >     vector (2048 bits) is much larger than the minimum size (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:
> >
> >       struct s { _Alignas(16) int i; };
> >
> >     except that the amount of padding would only be known at runtime.
> >
> >     There's also a significant conceptual problem: encoding a fixed size
> >     goes against a guiding principle of SVE, in which there is no preferred
> >     vector length.  There's 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.
> >
> > (3b) The vector types have a constant size and refer to separate storage
> >      (as for std::string etc.)
> >
> >     This would be difficult to do without C++-style constructor, destructor,
> >     copy and move semantics, so wouldn't work well in C.  And in C++ it would
> >     be less efficient than the proposed approach, since presumably an Allocator
> >     would be needed to allocate the separate storage.  It would also require
> >     a complicated ABI mapping to ensure that the vectors can still be passed
> >     and returned in registers.
> >
> > Chosen approach
> > ---------------
> >
> > We therefore took approach (1) and classified C and C++ types as "sized"
> > (having a measurable size when fully-defined) and "sizeless" (never
> > having a measurable size).  Sizeless types have no defined size,
> > alignment or layout at the language level, with those things becoming
> > purely an ABI-level detail.
> >
> > We then treated all sizeless types as permanently incomplete.
> > On its own, this would put them in a similar situation to "void"
> > (although they wouldn't be exactly the same, since there are some
> > specific rules for "void" that don't apply to incomplete types in
> > general).  We then relaxed specific rules until the types were actually
> > useful.
> >
> > Things in favour of (1)
> > -----------------------
> >
> > The reasons above were mostly negative, arriving at (1) by elimination.
> > A more positive justification of this approach is that it 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 language extensions.  By default,
> > any language extension would treat the new types like other incomplete
> > types and choose conservatively-correct behaviour.  It would then be
> > possible to relax the language extension if this default behaviour
> > turns out to be too restrictive.
> >
> > (That said, treating the types as permanently incomplete still won't
> > 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.)
> >
> >
> > The SVE types in more detail
> > ============================
> >
> > Arm has published an SVE "ACLE" that specifies the SVE types and intrinsic
> > functions in detail.  For reference this is available without registration
> > at:
> >
> >     https://static.docs.arm.com/100987/0000/acle_sve_100987_0000_00_en.pdf
> >
> > but I'll try to keep this self-contained.
> >
> > The ACLE defines a vector type sv<base>_t for each supported element type
> > <base>_t, so that the complete set is:
> >
> >     svint8_t      svint16_t     svint32_t     svint64_t
> >     svuint8_t     svuint16_t    svuint32_t    svuint64_t
> >                   svfloat16_t   svfloat32_t   svfloat64_t
> >
> > The types in each column have the same number of lanes and have twice
> > as many lanes as those in the column to the right.  Every vector has
> > the same number of bytes in total, with the number of bytes being
> > determined at runtime.
> >
> > The ACLE also defines a single predicate type:
> >
> >     svbool_t
> >
> > that has the same number of lanes as svint8_t and svuint8_t.
> >
> > All these types are opaque builtin types and are only expected to
> > be used with the associated ACLE intrinsics.  There are intrinsics for
> > creating vectors from scalars, loading from scalars, storing to scalars,
> > reinterpreting one type as another, etc.
> >
> > The idea is that the vector types would only be used for short-term
> > register-sized working data.  Longer-term data would typically be stored
> > out to arrays.
> >
> > For example, the vector function underlying:
> >
> >     #pragma omp declare simd
> >     double sin(double);
> >
> > would be:
> >
> >     svfloat64_t mangled_sin(svfloat64_t, svbool_t);
> >
> > (The svbool_t is because SVE functions should be predicated by default,
> > to avoid the need for a scalar epilogue loop.)
> >
> > The ACLE also defines x2, x3 and x4 tuple types for each vector type;
> > for example, svint8x3_t is a tuple of 3 svint8_ts.  The tuples are
> > structure-like types with fields v0, v1, v2 and v3, up to the number
> > required.
> >
> >
> > Outline of the type system changes
> > ==================================
> >
> > Going back to the summary at the start of the RFC, C classifies types as
> > "complete" (the size of objects can be calculated) or "incomplete" (the
> > size of objects can't be calculated).  There's very little you can do
> > with a type until it becomes complete.
> >
> > The approach we took was to treat all the SVE types as permanently
> > incomplete.  We then went through the standard relaxing specific
> > rules until the types were actually useful.
> >
> > The first step was to classify types as:
> >
> >   * "indefinite" (lacking sufficient information to create an object of
> >     that type) or "definite" (having sufficient information)
> >
> >   * "sized" (will have a known size when definite) or "sizeless" (will
> >     never have a known 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 unchanged from the standard.
> > Thus a "definite type" is one that has been fully-defined rather than
> > simply declared, and "complete" is now equivalent to "sized and definite".
> > All standard types are "sized" (even "void", although it's always
> > indefinite and incomplete).
> >
> > We then needed to make some rules use the distinction between "indefinite"
> > and "definite" rather than "incomplete" and "complete".  The specific
> > things we wanted to allow were:
> >
> >   * defining automatic variables with sizeless definite type
> >   * defining functions whose parameters have sizeless definite type
> >   * defining functions that return a sizeless definite type
> >   * using sizeless definite types in _Generic associations
> >   * dereferencing pointers to sizeless definite types
> >
> > Specific things we wanted to remain invalid -- by inheriting the rules from
> > incomplete types -- were:
> >
> >   * creating or accessing arrays that have sizeless element types
> >   * doing pointer arithmetic on pointers to sizeless types
> >   * using sizeof and _Alignof with a sizeless type (or object of sizeless type)
> >   * defining (sized) unions or structures with sizeless members
> >
> > It also seemed worth adding an extra restriction:
> >
> >   * variables with sizeless type must not have static or thread-local
> >     storage duration
> >
> > In practice it's impossible to define such variables with incomplete type,
> > but having an explicit rule means that things like:
> >
> >     extern svint8_t foo;  // An SVE vector of int8_t elements.
> >
> > are outright invalid rather than simply useless (because no other
> > translation unit could ever define foo).  Similarly, without an
> > explicit rule:
> >
> >     svint8_t foo;         // An SVE vector of int8_t elements.
> >
> > 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.
> >
> > This restriction isn't critical but it gives better diagnostics.
> >
> >
> > Sizeless structures (and testing on non-SVE targets)
> > ====================================================
> >
> > We're planning to build all SVE intrinsic types directly into GCC
> > (patches already written).  SVE therefore doesn't strictly need a syntax
> > for creating new sizeless types in C and C++.  However, having a way of
> > creating new structure-like "sizeless" types would be useful for three
> > reasons:
> >
> >   - Functions could return arbitrary data by value.  The SVE ABI allows
> >     a function to return up to 8 vectors and 4 predicates in registers,
> >     which is far more flexible than the intrinsic types.
> >
> >   - We could use these sizeless structure types to test the functionality
> >     on all targets.
> >
> >   - A lot of the C++ frontend is concerned with classes, and having
> >     a way of creating sizeless classes would help make the C++ changes
> >     more consistent.
> >
> > The patches therefore add a new "__sizeless_struct" keyword to denote
> > structures that are sizeless rather than sized.  Unlike normal
> > structures, these structures can have members of sizeless type in
> > addition to members of sized type.  On the other hand, they have all
> > the same limitations as other sizeless types (described in earlier
> > sections).
> >
> > E.g., a sizeless structure definition might look like:
> >
> >     __sizeless_struct data {
> >       double *array;
> >       svuint64_t indices;  // An SVE vector of uint64_t elements.
> >       svbool_t active;     // An SVE predicate.
> >     };
> >
> > Adding a new keyword seemed better than using an attribute because it
> > means that the sized vs. sizeless distinction is fixed by the declaration.
> > E.g.:
> >
> >     struct data;                     // Is it sized or sizeless?
> >     extern struct data global_data;  // OK if sized, not if sizeless.
> >     struct __attribute__((sizeless)) data {
> >       double *array;
> >       svuint64_t indices;            // An SVE vector of uint64_t elements.
> >       svbool_t active;               // An SVE predicate.
> >     };
> >
> > would lead to the declaration of "global_data" sneaking through
> > despite being invalid when "data" is sizeless.
> >
> > The tests in the patches all use these __sizeless_structs; they contain
> > nothing SVE- or AArch64-specific.
> >
> >
> > Other variable-length vector architectures
> > ==========================================
> >
> > The proposed RISC-V vector extension also has variable-length vectors.
> > When this language change was discussed on the clang developers' list,
> > Bruce Hoult (from SiFive, but speaking personally) replied with:
> >
> >     http://lists.llvm.org/pipermail/cfe-dev/2018-May/057943.html
> >
> > That message covers some of the background about the vector extension.
> > On the language changes, Bruce said:
> >
> >     > However, even though the length is variable, the concept of a
> >     > "register-sized" C and C++ vector type makes just as much sense for SVE
> >     > as it does for other vector architectures.  Vector library functions
> >     > take such register-sized vectors as input and return them as results.
> >     > Intrinsic functions are also just as useful as they are for other vector
> >     > architectures, and they too take register-sized vectors as input and
> >     > return them as results.
> >
> >     Intrinsic functions are absolutely required, and are I think the main
> >     reason for such a low-level register-sized vector type to exist.
> >
> > [ Bruce went on to say:
> >
> >     I'm not sure whether user-written functions operating on register-sized
> >     vectors are useful enough to support. User-written functions would normally
> >     take and return a higher-level vector type, and would implement the desired
> >     functionality in terms of calls to other user-written functions (operating
> >     on the high level vector as a whole) and/or explicit loops iterating
> >     through the high level vector type using intrinsic functions on the
> >     register-sized vector type proposed here.
> >
> > But this use case is very important for SVE, since it will allow us
> > to implement vector math routines in a way that works with the OpenMP
> > "declare simd" construct.  There was also talk on gcc@ recently about
> > supporting this style of interface for RISC-V. ]
> >
> > [...]
> >
> >     > All these types are opaque builtin types and are only intended to be
> >     > used with the associated ACLE intrinsics.  There are intrinsics for
> >     > creating vectors from scalars, loading from scalars, storing to scalars,
> >     > reinterpreting one type as another, etc.
> >     >
> >     > The idea is that the vector types would only be used for short-term
> >     > register-sized working data.  Longer-term data would typically be stored
> >     > out to arrays.
> >
> >     I agree with this.
> >
> > [...]
> >
> >     > The approach we took was to treat all the SVE types as permanently
> >     > incomplete.
> >
> >     This seems reasonable.
> >
> > So it looks like this extension would be useful for at least one
> > architecture besides SVE.
> >
> >
> > Edits to the C standard
> > =======================
> >
> > This section specifies the behaviour for sizeless types as an edit to N1570.
> > There are three stages:
> >
> >   - base changes, which add enough support for built-in sizeless
> >     vector types
> >
> >   - updates for consistency, which change some of the wording without
> >     changing the meaning
> >
> >   - support for sizeless structures
> >
> > In each case, -strikethrough- indicates deleted text and *bold*
> > includes additional text.
> >
> >
> > Base changes
> > ------------
> >
> > These changes are enough to support sizeless built-in vector types.
> >
> >     6.2.5 Types
> >     -----------
> >
> >     1. The meaning of a value stored in an object or returned by a
> >     function is determined by the type of the expression used to access
> >     it. … Types are partitioned into object types (types that
> >     describe objects) and function types (types that describe
> >     functions).  -At various points within a translation unit an object
> >     type may be incomplete (lacking sufficient information to determine
> >     the size of objects of that type) or complete (having sufficient
> >     information).37)- *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.*
> >
> >     1A. *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).37) 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.*
> >
> >     1B. *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.*
> >
> >     …
> >
> >     19. The void type comprises an empty set of values; it is -an
> >     incomplete- *a sized indefinite* object type that cannot be completed
> >     *(made definite)*.
> >
> >     …
> >
> >     37) A type may be -incomplete- *indefinite* or -complete- *definite*
> >     throughout an entire translation unit, or it may change states at
> >     different points within a translation unit.
> >
> >     …
> >
> >     6.3.2.1 Lvalues, arrays, and function designators
> >     -------------------------------------------------
> >
> >     1.  An lvalue is an expression (with an object type other than void)
> >     that potentially designates an object;64) … A modifiable lvalue is
> >     an lvalue that does not have array type, does not have an
> >     -incomplete- *indefinite* type, does not have a const-qualified
> >     type, …
> >
> >     2.  Except when it is the operand of the sizeof operator, the
> >     _Alignof operator, the unary & operator, the ++ operator, the --
> >     operator, or the left operand of the . operator or an assignment
> >     operator, an lvalue that does not have array type is converted to
> >     the value stored in the designated object (and is no longer an
> >     lvalue); this is called lvalue conversion. … If the lvalue has an
> >     -incomplete- *indefinite* type and does not have array type, the
> >     behavior is undefined. …
> >
> >     …
> >
> >     6.5.1.1 Generic selection
> >     -------------------------
> >
> >     …
> >
> >     Constraints
> >
> >     2. A generic selection shall have no more than one default generic
> >     association. The type name in a generic association shall specify a
> >     -complete- *definite* object type other than a variably modified
> >     type. …
> >
> >     …
> >
> >     6.5.2.2 Function calls
> >     ----------------------
> >
> >     Constraints
> >
> >     1. The expression that denotes the called function92) shall have
> >     type pointer to function returning void or returning a -complete-
> >     *definite* object type other than an array type.
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     4. An argument may be an expression of any -complete- *definite* object
> >     type. …
> >
> >     …
> >
> >     6.5.2.5 Compound literals
> >     -------------------------
> >
> >     Constraints
> >
> >     1. The type name shall specify a -complete- *definite* object type or an
> >     array of unknown size, but not a variable length array type.
> >
> >     …
> >
> >     6.7 Declarations
> >     ----------------
> >
> >     Constraints
> >
> >     …
> >
> >     4A. *If an identifier for an object does not have automatic storage
> >     duration, its type must be sized rather than sizeless.*
> >
> >     Semantics
> >
> >     …
> >
> >     7. If an identifier for an object is declared with no linkage, the
> >     type for the object shall be -complete- *definite* by the end of its
> >     declarator, or by the end of its init-declarator if it has an
> >     initializer; in the case of function parameters (including in
> >     prototypes), it is the adjusted type (see 6.7.6.3) that is required
> >     to be -complete- *definite*.
> >
> >     …
> >
> >     6.7.6.3 Function declarators (including prototypes)
> >     ---------------------------------------------------
> >
> >     Constraints
> >
> >     …
> >
> >     4. After adjustment, the parameters in a parameter type list in a
> >     function declarator that is part of a definition of that function
> >     shall not have -incomplete- *indefinite* type.
> >
> >     …
> >
> >     6.7.9 Initialization
> >     --------------------
> >
> >     Constraints
> >
> >     …
> >
> >     3. The type of the entity to be initialized shall be an array of
> >     unknown size or a -complete- *definite* object type that is not a
> >     variable length array type.
> >
> >     …
> >
> >     6.9.1 Function definitions
> >     --------------------------
> >
> >     Constraints
> >
> >     …
> >
> >     3. The return type of a function shall be void or a -complete-
> >     *definite* object type other than array type.
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     7. The declarator in a function definition specifies the name of the
> >     function being defined and the identifiers of its parameters. …
> >     [T]he type of each parameter is adjusted as described in
> >     6.7.6.3 for a parameter type list; the resulting type shall be a
> >     -complete- *definite* object type.
> >
> >     …
> >
> >     J.2 Undefined behavior
> >     ----------------------
> >
> >         …
> >       * A non-array lvalue with -an incomplete- *an indefinite* type is used
> >         in a context that requires the value of the designated object
> >         (6.3.2.1).
> >         …
> >       * An identifier for an object is declared with no linkage and the
> >         type of the object is -incomplete- *indefinite* after its
> >         declarator, or after its init-declarator if it has an
> >         initializer (6.7).
> >         …
> >       * An adjusted parameter type in a function definition is not a
> >         -complete- *definite* object type (6.9.1).
> >         …
> >
> > Updates for consistency
> > -----------------------
> >
> > These changes are a prerequisite for sizeless structures.  They have no
> > effect otherwise, but might be preferred anyway because they make the
> > terminology more consistent.  They apply on top of the previous edits.
> >
> >     6.2.5 Types
> >     -----------
> >
> >     …
> >
> >     22. An array type of unknown size is an -incomplete- *indefinite*
> >     type. It is -completed- *made definite*, for an identifier of that type,
> >     by specifying the size in a later declaration (with internal or
> >     external linkage). A structure or union type of unknown content (as
> >     described in 6.7.2.3) is an -incomplete- *indefinite* type. It is
> >     -completed- *made definite*, for all declarations of that type, by
> >     declaring the same structure or union tag with its defining content
> >     later in the same scope.
> >
> >     …
> >
> >     6.2.7 Compatible type and composite type
> >     ----------------------------------------
> >
> >     1. Two types have compatible type if their types are the same. …
> >     Moreover, two structure, union, or enumerated types declared in
> >     separate translation units are compatible if their tags and members
> >     satisfy the following requirements: If one is declared with a tag,
> >     the other shall be declared with the same tag. If both are
> >     -completed- *made definite* anywhere within their respective
> >     translation units, then the following additional requirements apply: …
> >
> >     …
> >
> >     6.7.2.1 Structure and union specifiers
> >     --------------------------------------
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     8. The presence of a struct-declaration-list in a
> >     struct-or-union-specifier declares a new type, within a translation
> >     unit. The struct-declaration-list is a sequence of declarations for
> >     the members of the structure or union.  If the struct-declaration-list
> >     does not contain any named members, either directly or via an anonymous
> >     structure or anonymous union, the behavior is undefined.  The type is
> >     -incomplete- *indefinite* until immediately after the } that terminates
> >     the list, and -complete- *definite* thereafter.
> >
> >     …
> >
> >     6.7.2.2 Enumeration specifiers
> >     ------------------------------
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     4. … The enumerated type is -incomplete- *indefinite* until
> >     immediately after the } that terminates the list of enumerator
> >     declarations, and -complete- *definite* thereafter.
> >
> >     …
> >
> >     6.7.2.3 Tags
> >     ------------
> >
> >     …
> >
> >     Semantics
> >
> >     4. All declarations of structure, union, or enumerated types that
> >     have the same scope and use the same tag declare the same
> >     type. Irrespective of whether there is a tag or what other
> >     declarations of the type are in the same translation unit, the type
> >     is -incomplete- *indefinite* 129) until immediately after the closing
> >     brace of the list defining the content, and -complete- *definite*
> >     thereafter.
> >
> >     …
> >
> >     8. If a type specifier of the form
> >
> >     struct-or-union identifier
> >
> >     occurs other than as part of one of the above forms, and no other
> >     declaration of the identifier as a tag is visible, then it declares
> >     an -incomplete- *indefinite* structure or union type, and declares the
> >     identifier as the tag of that type.131)
> >
> >     …
> >
> >     129) An -incomplete- *indefinite* type may only by used when -the
> >     size of an object- *the ability to create an object* of that type
> >     is not needed.  It is not needed, for example, when a typedef name
> >     is declared to be a specifier for a structure or union, or when a
> >     pointer to or a function returning a structure or union is being
> >     declared. (See -incomplete- *indefinite* types in 6.2.5.) The
> >     specification has to be -complete- *definite* before such a function
> >     is called or defined.
> >
> >     6.7.6.3 Function declarators (including prototypes)
> >     ---------------------------------------------------
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     12.  If the function declarator is not part of a definition of that
> >     function, parameters may have -incomplete- *indefinite* type and may use
> >     the [*] notation in their sequences of declarator specifiers to
> >     specify variable length array types.
> >
> >     …
> >
> >     J.2 Undefined behavior
> >     ----------------------
> >
> >         …
> >       * When the -complete- *definite* type is needed, an -incomplete-
> >         *indefinite* structure or union type is not completed in the same
> >         scope by another declaration of the tag that defines the content
> >         (6.7.2.3).
> >         …
> >
> > Sizeless structures
> > -------------------
> >
> > These additional changes to N1570 add the concept of a sizeless structure.
> > Again they apply on top of the edits above:
> >
> >     6.2.3 Name spaces of identifiers
> >     --------------------------------
> >
> >     1. If more than one declaration of a particular identifier is
> >     visible at any point in a translation unit, the syntactic context
> >     disambiguates uses that refer to different entities. Thus, there
> >     are separate name spaces for various categories of identifiers, as
> >     follows:
> >
> >       …
> >
> >       * the tags of *sized* structures, *sizeless structures,* unions, and
> >       enumerations (disambiguated by following any32) of the keywords
> >       struct, *__sizeless_struct,* union, or enum);
> >
> >       …
> >
> >     6.2.5 Types
> >     -----------
> >
> >     1. … Types are partitioned into object types (types that describe
> >     objects) and function types (types that describe functions).
> >     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.-
> >     *the only sizeless types defined by this standard are __sizeless_structs,
> >     but an implementation may provide additional sizeless types.*
> >
> >     …
> >
> >     1B. 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.
> >
> >     …
> >
> >     20. Any number of derived types can be constructed from the object
> >     and function types, as follows: …
> >
> >       * A *sized* structure type describes a sequentially allocated
> >         nonempty set of sized member objects (and, in certain
> >         circumstances, an incomplete array), each of which has an
> >         optionally specified name and possibly distinct type.
> >
> >       * *A sizeless structure type describes a set of non-overlapping
> >         member objects whose types may be sizeless and whose relative
> >         positions are unspecified.  It is also unspecified whether the
> >         structure occupies a single contiguous piece of storage or
> >         whether it requires several disjoint pieces.*
> >
> >     …
> >
> >     *20A. The term structure type refers collectively to sized structure
> >     types and sizeless structure types.*
> >
> >     …
> >
> >     6.4.1 Keywords
> >     --------------
> >
> >     Syntax
> >
> >     1. *(Add __sizeless_struct to the list and update the copy in A.1.2)*
> >
> >     …
> >
> >     6.5.8 Relational operators
> >     --------------------------
> >
> >     …
> >
> >     Semantics
> >
> >     …
> >
> >     5. When two pointers are compared, the result depends on the
> >     relative locations in the address space of the objects pointed to.
> >     … If the objects pointed to are members of the same aggregate object,
> >     pointers to *sized* structure members declared later compare greater
> >     than pointers to members declared earlier in the structure, and
> >     pointers to array elements with larger subscript values compare
> >     greater than pointers to elements of the same array with lower
> >     subscript values. …
> >
> >     …
> >
> >     6.7.2.1 Structure and union specifiers
> >     --------------------------------------
> >
> >     Syntax
> >
> >     struct-or-union-specifier:
> >         struct-or-union identifieropt { struct-declaration-list }
> >         struct-or-union identifier
> >
> >     struct-or-union:
> >         struct
> >         *__sizeless_struct*
> >         union
> >
> >     …
> >
> >     3. A *sized* structure or union shall not contain a member with
> >     incomplete or function type …, except that the last member of a
> >     structure with more than one named member may have incomplete array
> >     type; such a structure (and any union containing, possibly
> >     recursively, a member that is such a structure) shall not be a
> >     member of a structure or an element of an array.  *Simlarly, a
> >     sizeless structure shall not contain a member with indefinite or
> >     function type; the exception for incomplete array types does not
> >     apply.*
> >
> >     …
> >
> >     Semantics
> >
> >     6. As discussed in 6.2.5, a *sized* structure is a type consisting
> >     of a sequence of members, whose storage is allocated in an ordered
> >     sequence; *a sizeless structure is a type consisting of
> >     non-overlapping members whose relative position is unspecified,*
> >     and a union is a type consisting of a sequence of members whose
> >     storage overlap.
> >
> >     7. Structure and union specifiers have the same form. The keywords
> >     struct, *__sizeless_struct* and union indicate that the type being
> >     specified is, respectively, a *sized* structure type, *a sizeless
> >     structure type,* or a union type.
> >
> >     …[8 is as above]…
> >
> >     9. A member of a structure or union may have any complete object
> >     type other than a variably modified type.123)  *A member of a sizeless
> >     structure may also have a sizeless definite type.*  In addition, a
> >     member *of a structure or union* may be declared to consist of a
> >     specified number of bits (including a sign bit, if any). Such a
> >     member is called a bit-field;124) its width is preceded by a colon.
> >
> >     …
> >
> >     15. Within a *sized* structure object, the non-bit-field members and
> >     the units in which bit-fields reside have addresses that increase in
> >     the order in which they are declared. A pointer to a *sized* structure
> >     object, suitably converted, points to its initial member (or if that
> >     member is a bit-field, then to the unit in which it resides), and
> >     vice versa. There may be unnamed padding within a *sized* structure
> >     object, but not at its beginning.
> >
> >     15A. *The representation of a sizeless structure object is
> >     unspecified.  It is possible to form pointers to the structure
> >     itself and to its individual members, but the relationship between
> >     their addresses is unspecified.  The structure may occupy a single
> >     piece of contiguous storage or it may occupy several disjoint
> >     pieces.*
> >
> >     …
> >
> >     18 As a special case, the last element of a *sized* structure with
> >     more than one named member may have an incomplete array type; this
> >     is called a flexible array member. …
> >
> >     …
> >
> >     6.7.2.3 Tags
> >     ------------
> >
> >     Constraints
> >
> >     …
> >
> >     2. Where two declarations that use the same tag declare the same
> >     type, they shall both use the same choice of struct, *__sizeless_struct,*
> >     union, or enum.
> >
> >     …
> >
> >
> > Edits to the C++ standard
> > =========================
> >
> > We have a similar set of changes to the C++ standard, but this RFC is
> > long enough already, so I've not included them here.  I also didn't find
> > them to be particularly useful when writing the C++ patches, since most
> > of the changes were obvious given a few basic rules.  Those rules are:
> >
> >   - type traits can be used with sizeless types (unlike incomplete types)
> >
> >   - sizeless structures cannot have base classes or be used as base classes
> >
> >   - sizeless structures cannot have virtual members
> >
> >   - pointers to member variables are invalid for sizeless structures
> >     (although taking the address of a member of a specific sizeless object
> >     is fine, as for C)
> >
> >   - sizeless types are not literal types
> >
> >   - sizeless types cannot be created by operator new (as for incomplete types)
> >
> >   - sizeless types cannot be deleted (so, unlike for incomplete types,
> >     this is an error rather than a warning)
> >
> >   - sizeless types cannot be thrown or caught (as for incomplete types)
> >
> >   - sizeless types cannot be used with typeid() (as for incomplete types)
> >
> >
> > GCC implementation questions
> > ============================
> >
> > The GCC patches are pretty simple in principle.  The language changes
> > involve going through the standard replacing "complete" with "definite"
> > and most of the GCC patches go through the frontend code making the
> > same kind of change.
> >
> > New type flag for sizeless types
> > --------------------------------
> >
> > The patches add a new flag TYPE_SIZELESS_P to represent the negative of:
> >
> >   * would fully-defining the type determine its size?
> >
> > from the summary above.  Negative names are usually a bad thing,
> > but the natural default is for the flag to be off.
> >
> > There are currently 17 bits free in tree_type_common, so the patches
> > steal one of those.  Is that OK?
> >
> > The effect on COMPLETE_TYPE_P
> > -----------------------------
> >
> > The current definition of COMPLETE_TYPE_P is:
> >
> >     /* Nonzero if this type is a complete type.  */
> >     #define COMPLETE_TYPE_P(NODE) (TYPE_SIZE (NODE) != NULL_TREE)
> >
> > Although the SVE types don't have a measurable size at the language
> > level, they still have a TYPE_SIZE and TYPE_SIZE_UNIT, with the sizes
> > using placeholders for the runtime vector size.  So after the split
> > described in the summary, TYPE_SIZE (NODE) != NULL_TREE means
> > "the type is fully defined" rather than "the type is complete".
> > With TYPE_SIZELESS_P, the definition of "complete type" would be:
> >
> >     #define COMPLETE_TYPE_P(NODE) \
> >       (TYPE_SIZE (NODE) != NULL_TREE && !TYPE_SIZELESS_P (NODE))
> >
> > i.e. the type is fully-defined, and fully-defining it determines
> > its size at the language level.
> >
> > Uses of COMPLETE_TYPE_P outside the frontends
> > ---------------------------------------------
> >
> > The main complication is that the concept of "complete type" is exposed
> > outside the frontends, with COMPLETE_TYPE_P being defined in tree.h.
> >
> > I tried to audit all uses outside the frontends and it looks like
> > they're all testing whether "the type is fully defined" and don't
> > care about the distinction between sized and sizeless.  This means
> > that the current definition (rather than the new definition)
> > should be correct in all cases.
> >
> > In some cases the tests are simple null checks, like:
> >
> >      /* Try to approach equal type sizes.  */
> >      if (!COMPLETE_TYPE_P (type_a)
> >          || !COMPLETE_TYPE_P (type_b)
> >          || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_a))
> >          || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type_b)))
> >        break;
> >
> > IMO it's more obvious to test TYPE_SIZE_UNIT directly for null here.
> > Having a wrapper doesn't add much.
> >
> > In places like:
> >
> >   if (!COMPLETE_TYPE_P (t))
> >     layout_type (t);
> >
> > and:
> >
> >   if (COMPLETE_TYPE_P (t) && TYPE_CANONICAL (t)
> >       && TYPE_MODE (t) != TYPE_MODE (TYPE_CANONICAL (t)))
> >     ...
> >
> > it's testing whether the type has been laid out already.
> >
> > So the patches do two things:
> >
> >   * Expand the definition of the current COMPLETE_TYPE_P macro outside
> >     the frontends if the macro is simply protecting against a null
> >     dereference.
> >
> >   * Make COMPLETE_TYPE_P local to the frontends and rename all uses
> >     outside the frontends.
> >
> > As far as the second point goes, I wasn't sure what new name to use
> > outside the front ends.  Possibilities include:
> >
> >   - DEFINITE_TYPE_P
> >   - USABLE_TYPE_P
> >   - VALID_VAR_TYPE_P
> >   - TYPE_LAID_OUT_P
> >   - TYPE_DEFINED_P
> >   - TYPE_FULLY_DEFINED_P
> >   - TYPE_READY_P
> >   ...other suggestions welcome...
> >
> > I went for DEFINITE_TYPE_P because that's what the SVE specification
> > uses, but something more neutral like TYPE_DEFINED_P might be better.
> >
> > Frontend changes
> > ----------------
> >
> > The frontend patches change COMPLETE_TYPE_P to DEFINITE_TYPE_P where
> > necessary.  I've tried where possible to accompany each individual
> > change with a test.
> >
> > This worked fairly naturally (IMO) for C, and most of the changes could
> > be tied directly to the language edits above.
> >
> > For C++ it was more difficult (not surprisingly).  There are a lot of
> > tests for COMPLETE_TYPE_P that are obviously testing whether a class
> > has been fully defined, and are more concerned with name lookup than
> > TYPE_SIZE.  The same goes for COMPLETE_OR_OPEN_TYPE_P and whether the
> > definition has been started.  So while the C changes were relatively
> > small and self-contained, the C++ changes replace many more uses of
> > COMPLETE_TYPE_P than they keep.  This makes me wonder whether it's a
> > good idea to keep COMPLETE_TYPE_P at all, or whether it would be better
> > to replace the remaining uses with something more explicit like:
> >
> >   TYPE_SIZE_KNOWN_P
> >   TYPE_SIZE_DEFINED_P
> >   TYPE_SIZE_MEASURABLE_P
> >   TYPE_SIZE_COMPLETE_P
> >   ...suggestions again welcome...
> >
> > Thanks,
> > Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-15 18:57 ` Uecker, Martin
  2018-10-16  8:51   ` Richard Biener
@ 2018-10-16 12:55   ` Richard Sandiford
  2018-10-16 23:07     ` Joseph Myers
  1 sibling, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-16 12:55 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: gcc-patches, jason, nd, nathan, joseph

Hi Martin,

Thanks for the reply.

"Uecker, Martin" <Martin.Uecker@med.uni-goettingen.de> writes:
> Hi Richard,
>
> as Joseph pointed out, there are some related discussions
> on the WG14 reflector. How a about moving the discussion
> there?

The idea was to get a feel for what would be acceptable to GCC
maintainers.  When Arm presented an extension of P0214 to support SVE
at the last C++ committee meeting, using this sizeless type extension
as a possible way of providing the underlying vector types, the feeling
seemed to be that it wouldn't be considered unless it had already been
proven in compilers.

> I find your approach very interesting and that it already
> comes with an implementation is of course very useful
>
> But I don't really understand the reasons why this is not based
> on (2). These types are not "sizeless" at all, their size
> just isn't known at compile time. So to me this seems to me
> a misnomer.
>
> In fact, to me these types *do* in fact seem very similar
> to VLAs as VLAs are also complete types which also do no
> have a known size at compile time.
>
> That arrays decay to pointers doesn't mean that we
> couldn't have similar vectors types which don't decay.
> This is hardly a fundamental problem.

I think it is for some poople though.  If the vectors don't decay to
pointers, they're moe akin to a VLA wrapped in a structure rather than
a stand-alone VLA.  There is a GNU extension for that, e.g.:

  int
  f (int n)
  {
    struct s {
      int x[n];
    } foo;
    return sizeof (foo.x);
  }

But even though clang supports VLAs (of course), it rejects the
above with:

  error: fields must have a constant size: 'variable length array in structure' extension will never be supported

This gives a strong impression that wrapping a VLA type like this
is a bridge too far for some :-)  The message makes it clear that's
a case of "don't even bother asking".

The vector tuple types would be very similar to this if modelled as VLAs.

> I also don't understand the problem about the array
> size. If I understand this correctly, the size is somehow
> known at run-time and implicitly passed along with the
> values. So these new types do not need to have a
> size expression (as in your proposal). 

The problem isn't so much that the size is only known at runtime,
but that the size isn't necessarily invariant, and the size of an
object doesn't carry the size information with it.

This means you can't tell what size a given object is, even at runtime.
All you can tell is what size the object would be if you created it
from scratch.  E.g.:

  svint8_t *ptr;  // pointer to variable-length vector type

  void thread1 (void)
  {
    svint8_t local;
    *ptr = &local;
    ...run for a long time...
  }

  void thread2 (void)
  {
    ... sizeof (*ptr); ...;
  }

If thread1 and thread2 have different vector lengths, thread2 has no way
of knowing what size *ptr is.

Of course, thread2 can't validly use *ptr if it has wider vectors than
thread1, but if we resort to saying "undefined behavior" for the above,
then it becomes difficult to define when the size actually is defined.
It's simpler not to make it measurable via sizeof at all.  (And that's
a much less invasive change to the language.)

> Assignment, the possibility to return the type from
> functions, and something like __sizeless_structs would
> make sense for VLAs too.
>
> So creating a new category "variable-length types" for 
> both VLAs and variably-length vector types seems do make
> much more sense to me. As I see it, this would be mainly
> a change in terminology and not so much of the underlying
> approach.

But do you have any feel for whether this would ever be acceptable
in C++?  One of the main requirements for this was that it needs
to work in both C and C++, with the same ABI representation.
I thought VLAs were added to an early draft of C++14 and then
removed before it was published.  They weren't added back for C++17,
and I'd seen other proposals about classes having a "sizeof field"
instead (i.e. the type would carry the size information with it,
which we don't want).  So the prospects didn't look good.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-16 12:55   ` Richard Sandiford
@ 2018-10-16 23:07     ` Joseph Myers
  2018-10-17 12:54       ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Joseph Myers @ 2018-10-16 23:07 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Uecker, Martin, gcc-patches, jason, nd, nathan

On Tue, 16 Oct 2018, Richard Sandiford wrote:

> > as Joseph pointed out, there are some related discussions
> > on the WG14 reflector. How a about moving the discussion
> > there?
> 
> The idea was to get a feel for what would be acceptable to GCC
> maintainers.  When Arm presented an extension of P0214 to support SVE
> at the last C++ committee meeting, using this sizeless type extension
> as a possible way of providing the underlying vector types, the feeling
> seemed to be that it wouldn't be considered unless it had already been
> proven in compilers.

But as shown in the related discussions, there are other possible features 
that might also involve non-VLA types whose size is not a compile-time 
constant.  And so it's necessary to work with the people interested in 
those features in order to clarify what the underlying concepts ought to 
look like to support different such features.

> I think it is for some poople though.  If the vectors don't decay to
> pointers, they're moe akin to a VLA wrapped in a structure rather than
> a stand-alone VLA.  There is a GNU extension for that, e.g.:
> 
>   int
>   f (int n)
>   {
>     struct s {
>       int x[n];
>     } foo;
>     return sizeof (foo.x);
>   }
> 
> But even though clang supports VLAs (of course), it rejects the
> above with:
> 
>   error: fields must have a constant size: 'variable length array in structure' extension will never be supported
> 
> This gives a strong impression that wrapping a VLA type like this
> is a bridge too far for some :-)  The message makes it clear that's
> a case of "don't even bother asking".

What are the clang concerns about VLAs in structs that are the reason for 
not supporting them?  How do the sizeless structs with sizeless members in 
your proposal avoid those concerns about the definition of VLAs in 
structs?

> The problem isn't so much that the size is only known at runtime,
> but that the size isn't necessarily invariant, and the size of an
> object doesn't carry the size information with it.
> 
> This means you can't tell what size a given object is, even at runtime.

How then is e.g. passing a pointer to such a struct (containing such 
unknown-size members) to another function supposed to work?  Or is there 
something in your proposed standard text edits that would disallow passing 
such a pointer, or disallow using "->" with it to access members?

> All you can tell is what size the object would be if you created it
> from scratch.  E.g.:
> 
>   svint8_t *ptr;  // pointer to variable-length vector type
> 
>   void thread1 (void)
>   {
>     svint8_t local;
>     *ptr = &local;
>     ...run for a long time...
>   }
> 
>   void thread2 (void)
>   {
>     ... sizeof (*ptr); ...;
>   }
> 
> If thread1 and thread2 have different vector lengths, thread2 has no way
> of knowing what size *ptr is.
> 
> Of course, thread2 can't validly use *ptr if it has wider vectors than
> thread1, but if we resort to saying "undefined behavior" for the above,
> then it becomes difficult to define when the size actually is defined.

What in your standard text edits serves to make that undefined?  
Generally, what in those edits serves to say when conversions involving 
such types, or pointers thereto, or accesses through compatible types in 
different places, are or are not defined?

In standard C, for example, we have for VLAs 6.7.6.2#6, "If the two array 
types are used in a context which requires them to be compatible, it is 
undefined behavior if the two size specifiers evaluate to unequal 
values.".  What is the analogue of this for sizeless types?  Since unlike 
VLAs you're allowing these types, and sizeless structs containing them, to 
be passed by value, assigned, etc., you need something like that to 
determine whether assignment, conditional expression, function argument 
passing, function return, access via a pointer, etc., are valid.

Can these types be used with _Atomic?  I don't see anything to say they 
can't.

Can these types be passed to variadic functions and named in va_arg?  
Again, I don't see anything to say they can't.

Can you have file-scope, and so static-storage-duration, compound literals 
with these types?  You're allowing compound literals with these types, and 
what you have disallowing objects with these types with non-automatic 
storage duration seems to be specific to the case of "an identifier for an 
object".

I don't see any change proposed to 6.2.6.1#2 corresponding to what you say 
elsewhere about discontiguous representations of sizeless structures.

> But do you have any feel for whether this would ever be acceptable
> in C++?  One of the main requirements for this was that it needs
> to work in both C and C++, with the same ABI representation.
> I thought VLAs were added to an early draft of C++14 and then
> removed before it was published.  They weren't added back for C++17,
> and I'd seen other proposals about classes having a "sizeof field"
> instead (i.e. the type would carry the size information with it,
> which we don't want).  So the prospects didn't look good.

What were the C++ concerns with VLAs, and how do the variable-size types 
in this proposal avoid those concerns?

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-16 23:07     ` Joseph Myers
@ 2018-10-17 12:54       ` Richard Sandiford
  2018-10-17 14:39         ` Joseph Myers
  2018-10-17 14:39         ` Uecker, Martin
  0 siblings, 2 replies; 31+ messages in thread
From: Richard Sandiford @ 2018-10-17 12:54 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Uecker, Martin, gcc-patches, jason, nathan

[ Sorry that there were so many typos in my last reply, will try to do better
  this time... ]

Joseph Myers <joseph@codesourcery.com> writes:
> On Tue, 16 Oct 2018, Richard Sandiford wrote:
>> The patches therefore add a new "__sizeless_struct" keyword to denote
>> structures that are sizeless rather than sized.  Unlike normal
>> structures, these structures can have members of sizeless type in
>> addition to members of sized type.  On the other hand, they have all
>> the same limitations as other sizeless types (described in earlier
>> sections).
>
> I don't see anything here disallowing offsetof on such structures.

I didn't think this needed to be done explicitly since:

	offsetof(type, member-designator)

    which expands to an integer constant expression that has type size_t,
    the value of which is the offset in bytes, to the structure
    member (designated by member-designator), from the beginning of its
    structure (designated by type). The type and member designator shall be
    such that given

        static type t;

    then the expression &(t.member-designator) evaluates to an address
    constant. (If the specified member is a bit-field, the behavior is
    undefined.)

implicitly rejects sizeless types on the basis that "static type t;"
would be invalid.  I think that's the same way that it rejects
incomplete structure types.

But yeah, it looks like I forgot to handle this in GCC. :-(

> On Tue, 16 Oct 2018, Richard Sandiford wrote:
>> > as Joseph pointed out, there are some related discussions
>> > on the WG14 reflector. How a about moving the discussion
>> > there?
>> 
>> The idea was to get a feel for what would be acceptable to GCC
>> maintainers.  When Arm presented an extension of P0214 to support SVE
>> at the last C++ committee meeting, using this sizeless type extension
>> as a possible way of providing the underlying vector types, the feeling
>> seemed to be that it wouldn't be considered unless it had already been
>> proven in compilers.
>
> But as shown in the related discussions, there are other possible features 
> that might also involve non-VLA types whose size is not a compile-time 
> constant.  And so it's necessary to work with the people interested in 
> those features in order to clarify what the underlying concepts ought to 
> look like to support different such features.

Could you give pointers to the specific proposals/papers you mean?

>> I think it is for some people though.  If the vectors don't decay to
>> pointers, they're more akin to a VLA wrapped in a structure rather than
>> a stand-alone VLA.  There is a GNU extension for that, e.g.:
>> 
>>   int
>>   f (int n)
>>   {
>>     struct s {
>>       int x[n];
>>     } foo;
>>     return sizeof (foo.x);
>>   }
>> 
>> But even though clang supports VLAs (of course), it rejects the
>> above with:
>> 
>>   error: fields must have a constant size: 'variable length array in structure' extension will never be supported
>> 
>> This gives a strong impression that wrapping a VLA type like this
>> is a bridge too far for some :-)  The message makes it clear that's
>> a case of "don't even bother asking".
>
> What are the clang concerns about VLAs in structs that are the reason for 
> not supporting them?

The user manual says:

-  clang does not support the gcc extension that allows variable-length
   arrays in structures. This is for a few reasons: one, it is tricky to
   implement, two, the extension is completely undocumented, and three,
   the extension appears to be rarely used. Note that clang *does*
   support flexible array members (arrays with a zero or unspecified
   size at the end of a structure).

So I guess defining it would remove the second objection.

> How do the sizeless structs with sizeless members in your proposal
> avoid those concerns about the definition of VLAs in structs?

The key difference is that the size, offset and layout don't have to be
known to the frontend and available during semantic analysis (unlike for
VLAs in structs).  In the clang implementation of sizeless types those
details only start to matter when translating clang ASTs into LLVM IR.
(With GCC it's a bit different, since TYPE_SIZE is set as soon as the
type definition is complete, even though for SVE TYPE_SIZE should only
matter in the mid and backend.)

>> The problem isn't so much that the size is only known at runtime,
>> but that the size isn't necessarily invariant, and the size of an
>> object doesn't carry the size information with it.
>> 
>> This means you can't tell what size a given object is, even at runtime.
>
> How then is e.g. passing a pointer to such a struct (containing such 
> unknown-size members) to another function supposed to work?  Or is there 
> something in your proposed standard text edits that would disallow passing 
> such a pointer, or disallow using "->" with it to access members?

The idea here...

>> All you can tell is what size the object would be if you created it
>> from scratch.  E.g.:
>> 
>>   svint8_t *ptr;  // pointer to variable-length vector type
>> 
>>   void thread1 (void)
>>   {
>>     svint8_t local;
>>     *ptr = &local;
>>     ...run for a long time...
>>   }
>> 
>>   void thread2 (void)
>>   {
>>     ... sizeof (*ptr); ...;
>>   }
>> 
>> If thread1 and thread2 have different vector lengths, thread2 has no way
>> of knowing what size *ptr is.
>> 
>> Of course, thread2 can't validly use *ptr if it has wider vectors than
>> thread1, but if we resort to saying "undefined behavior" for the above,
>> then it becomes difficult to define when the size actually is defined.
>
> What in your standard text edits serves to make that undefined?  
> Generally, what in those edits serves to say when conversions involving 
> such types, or pointers thereto, or accesses through compatible types in 
> different places, are or are not defined?
>
> In standard C, for example, we have for VLAs 6.7.6.2#6, "If the two array 
> types are used in a context which requires them to be compatible, it is 
> undefined behavior if the two size specifiers evaluate to unequal 
> values.".  What is the analogue of this for sizeless types?  Since unlike 
> VLAs you're allowing these types, and sizeless structs containing them, to 
> be passed by value, assigned, etc., you need something like that to 
> determine whether assignment, conditional expression, function argument 
> passing, function return, access via a pointer, etc., are valid.

...and here is that any size changes come only from changes in the
implementation-defined built-in sizeless types.  The user can't define
a new type whose size varies in new ways.  E.g. the size and layout of:

    __sizeless_struct s1 { svuint32_t v0, v1; };

can only vary because the size of the implementation-defined svuint32_t
can vary.  Something like:

    __sizeless_struct s2 { uint32_t v0, v1; };

would never change size or layout dynamically and the above scenarios
would be valid whenever they would be valid for "struct s2".

Since the built-in types are implementation-defined, the idea was
that the situations in which their size could vary should be
implementation-defined as well.

So passing a pointer to a sizeless struct containing a vector is OK if
nothing causes the vector length to change in the meantime.  The exact
circumstances in which that could happen are implementation-defined.
The same goes for the thread example above: in normal SVE usage it
would be fine for thread2 to use the vector at *ptr, since normally
all threads would run with the same vector length.  But there are some
implementation-defined situations in which the length could be different.

The situations in which the vector length can change for SVE (and thus
the situations in which the above examples would be undefined behaviour)
are very SVE-specific.  It's likely that the rules for any future sizeless
types would also be very specific to those types.  E.g. my understanding
of the proposed RISC-V vector extension is that the vector length would
change much more often than it does for SVE.

> Can these types be used with _Atomic?  I don't see anything to say they 
> can't.

At the moment that's allowed, although of course it probably isn't
useful in practice.

> Can these types be passed to variadic functions and named in va_arg?  
> Again, I don't see anything to say they can't.

Yes, this is allowed (and covered by the tests FWIW).

> Can you have file-scope, and so static-storage-duration, compound literals 
> with these types?  You're allowing compound literals with these types, and 
> what you have disallowing objects with these types with non-automatic 
> storage duration seems to be specific to the case of "an identifier for an 
> object".

Ah, you mean something like:

    typedef __sizeless_struct s { int i; } s;
    s *ptr = &(s) { 1 };

?  Yeah, good point, hadn't thought of that.  That should be invalid too.

> I don't see any change proposed to 6.2.6.1#2 corresponding to what you say 
> elsewhere about discontiguous representations of sizeless structures.

Yeah, good point.  It should be edited to say:

    Except for bit-fields *and sizeless structures*, objects are
    composed of contiguous sequences of one or more bytes, the number,
    order, and encoding of which are either explicitly specified or
    implementation-defined.

TBH the possibility of a discontiguous representation was an early idea
that we've never actually used so far, so if that's a problem, we could
probably drop it.  It just seemed to be a natural extension of the
principle that the layout is completely implementation-defined.

>> But do you have any feel for whether this would ever be acceptable
>> in C++?  One of the main requirements for this was that it needs
>> to work in both C and C++, with the same ABI representation.
>> I thought VLAs were added to an early draft of C++14 and then
>> removed before it was published.  They weren't added back for C++17,
>> and I'd seen other proposals about classes having a "sizeof field"
>> instead (i.e. the type would carry the size information with it,
>> which we don't want).  So the prospects didn't look good.
>
> What were the C++ concerns with VLAs,

The sizeless type proposal was drawn up while SVE was still
confidential, so it wasn't something we could openly talk about.
At the time it wasn't obvious from the online trail why VLAs
(well, ARBs) were removed.  The proposal was:

   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3639.html

which says that it "was approved by EWG during the Portland meeting of
WG21 and was updated to reflect CWG review".  At the time we were doing
the sizeless type proposal, we were in pretty much the same situation
as this reddit poster:

    https://www.reddit.com/r/cpp_questions/comments/3clm34/why_was_n3639_runtimesized_arrays_with_automatic/

in that all we could find was a statement of its removal, with no
explanation of the reasons.

I see now there's a stackoverflow answer with some of the history
(this postdates the original sizeless type work):

    https://stackoverflow.com/questions/40633344/variable-length-arrays-in-c14

It ends with:

    Sadly [...] there are no future plans to resurrect ARBs/VLAs with
    C++ in the simple c99 VLA form.

> and how do the variable-size types in this proposal avoid those concerns?

I think the key difference between sizeless types and ARBs is that
ARBs were intended to provide user-controlled sources of variability.
That would only be useful if having variable-length arrays as part of
the core language rather than the library is useful.  Here we're simply
providing canned built-in types with a fixed source of variability,
with that variability not being modelled in the language at all.
Users can combine those canned types together but they can't create
new sources of variability.  The sizeless type proposal is mostly just
a means to an end: a core language change that allows new library
extensions to be defined for targets like SVE.

I think the key difference between sizeless types and full C99-style
VLAs is that the size and layout of sizeless types never matters for
semantic analysis.  Rather than the sizes of types becoming variable
(and the offsets of members becoming variable, and constexprs becoming
variable-sized, etc.), we simply don't make those concepts available
for sizeless types.

So nothing at the language level becomes variable that was constant before.
All that happens is that some things become invalid for sizeless types
that would be valid for sized ones.

The idea was really for the language to provide a framework for
implementations to define implementation-specific types with
implementation-specific rules while disturbing the language itself
as little as possible.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 12:54       ` Richard Sandiford
  2018-10-17 14:39         ` Joseph Myers
@ 2018-10-17 14:39         ` Uecker, Martin
  2018-10-17 15:06           ` Richard Sandiford
  1 sibling, 1 reply; 31+ messages in thread
From: Uecker, Martin @ 2018-10-17 14:39 UTC (permalink / raw)
  To: richard.sandiford, joseph; +Cc: gcc-patches, jason, nathan



Am Mittwoch, den 17.10.2018, 13:30 +0100 schrieb Richard Sandiford:
> [ Sorry that there were so many typos in my last reply, will try to
> do better
>   this time... ]

...
> I think the key difference between sizeless types and full C99-style
> VLAs is that the size and layout of sizeless types never matters for
> semantic analysis.  Rather than the sizes of types becoming variable
> (and the offsets of members becoming variable, and constexprs
> becoming
> variable-sized, etc.), we simply don't make those concepts available
> for sizeless types.
> 
> So nothing at the language level becomes variable that was constant
> before.
> All that happens is that some things become invalid for sizeless
> types
> that would be valid for sized ones.
> 
> The idea was really for the language to provide a framework for
> implementations to define implementation-specific types with
> implementation-specific rules while disturbing the language itself
> as little as possible.

I guess this makes it much easier for C++, but also much less useful.
Surely, the processor knows the size when it computes using these
types, so one could make it available using 'sizeof'. If a value
of the type is stores in addressable memory (on the stack), can't
we also store the size so that it is available for other threads? 
Just making it undefined to access a variable with the wong size
for the current thread seems rather fragile to me. 

Best,
Martin






^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 12:54       ` Richard Sandiford
@ 2018-10-17 14:39         ` Joseph Myers
  2018-10-18 12:01           ` Richard Sandiford
  2018-10-17 14:39         ` Uecker, Martin
  1 sibling, 1 reply; 31+ messages in thread
From: Joseph Myers @ 2018-10-17 14:39 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Uecker, Martin, gcc-patches, jason, nathan

On Wed, 17 Oct 2018, Richard Sandiford wrote:

> > But as shown in the related discussions, there are other possible features 
> > that might also involve non-VLA types whose size is not a compile-time 
> > constant.  And so it's necessary to work with the people interested in 
> > those features in order to clarify what the underlying concepts ought to 
> > look like to support different such features.
> 
> Could you give pointers to the specific proposals/papers you mean?

They're generally reflector discussions rather than written up as papers, 
exploring the space of problems and solutions in various areas (including 
bignums and runtime introspection of types).  I think the first message in 
those discussions is number 15529 
<http://www.open-std.org/jtc1/sc22/wg14/15529> and then relevant 
discussions continue for much of the next 200 messages or so.

> ...and here is that any size changes come only from changes in the
> implementation-defined built-in sizeless types.  The user can't define

But then I think you still need to define in the standard edits something 
of what the type-compatibility rules are.

> > Can these types be passed to variadic functions and named in va_arg?  
> > Again, I don't see anything to say they can't.
> 
> Yes, this is allowed (and covered by the tests FWIW).

How does that work with not knowing the size even at runtime?

At least, this seems like another place where there would be special type 
compatibility considerations that need to be applied between caller and 
callee.

>     Except for bit-fields *and sizeless structures*, objects are
>     composed of contiguous sequences of one or more bytes, the number,
>     order, and encoding of which are either explicitly specified or
>     implementation-defined.
> 
> TBH the possibility of a discontiguous representation was an early idea
> that we've never actually used so far, so if that's a problem, we could
> probably drop it.  It just seemed to be a natural extension of the
> principle that the layout is completely implementation-defined.

If you have discontiguous representations, I don't see how "->" on 
structure pointers (or indeed unary "*") is supposed to work; disallowing 
discontiguous representations would seem to fit a lot more naturally with 
the C object model.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 14:39         ` Uecker, Martin
@ 2018-10-17 15:06           ` Richard Sandiford
  2018-10-17 15:25             ` Joseph Myers
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-17 15:06 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: joseph, gcc-patches, jason, nathan

"Uecker, Martin" <Martin.Uecker@med.uni-goettingen.de> writes:
> Am Mittwoch, den 17.10.2018, 13:30 +0100 schrieb Richard Sandiford:
>> [ Sorry that there were so many typos in my last reply, will try to
>> do better
>>   this time... ]
>
> ...
>> I think the key difference between sizeless types and full C99-style
>> VLAs is that the size and layout of sizeless types never matters for
>> semantic analysis.  Rather than the sizes of types becoming variable
>> (and the offsets of members becoming variable, and constexprs
>> becoming
>> variable-sized, etc.), we simply don't make those concepts available
>> for sizeless types.
>> 
>> So nothing at the language level becomes variable that was constant
>> before.
>> All that happens is that some things become invalid for sizeless
>> types
>> that would be valid for sized ones.
>> 

>> The idea was really for the language to provide a framework for
>> implementations to define implementation-specific types with
>> implementation-specific rules while disturbing the language itself
>> as little as possible.
>
> I guess this makes it much easier for C++, but also much less useful.

Yeah, can't deny that if you look at it as a general-purpose extension.
But that's not really what this is supposed to be.  It's fairly special
purpose: there has to be some underlying variable-length/sizeless
built-in type that you want to provide via a library.

What the extension allows is enough to support the intended use case,
and it does that with no enforced overhead.

The examples with one thread accessing a vector from a different thread
aren't interesting in practice; any sharing or caching should happen via
normal arrays or malloced memory instead.  These corner cases are just
something that needs to be addressed once you allow pointers to things.

> Surely, the processor knows the size when it computes using these
> types, so one could make it available using 'sizeof'.

The argument's similar here: we don't really need sizeof to be available
for vector use because the library provides easy ways of getting
vector-length-based constants.  Usually what you want to know is
"how many elements of type X are there?", with bytes just being one
of the available element sizes.

And of course, making sizeof variable would be a can of worms in C++...
(The rejected ARB proposal didn't try to do that.)

> If a value of the type is stores in addressable memory (on the stack),
> can't we also store the size so that it is available for other threads? 

But the problem is that once the size becomes a part of the object,
it becomes difficult to get rid of it again for the intended use case.
E.g. say a vector is passed on the stack due to running out of registers.
Does the caller provide both the size and the contents, or just the
contents?  In the latter case it would be the callee's reponsibility
to "fill in" the missing size, but at what point should it compute the
size?  In the former case, would a callee-copies ABI require the callee
to copy the contents with the size given in the argument or the value
that the callee would use if it were creating an object from scratch?

These aren't insurmountable problems.  They just seem like an unnecessary
complication when the only reason for doing them is to support sizeof,
which isn't something that the use case needs.

Also, storing the size with the object would make the size field
*become* part of the size of the object, so sizeof (vector) would give
you something bigger than the size of the vector itself.  It would also
open up oddities like:

  sizeof (x) != sizeof (typeof (x))

being false in some cases.  I.e. even if sizeof (x) correctly reported
the size that an object x actually has, it isn't necessarily the size
that a new object of that type would have, so users would have to be
very careful about what they ask.  I think both these things would just
open the door to more confusion.

> Just making it undefined to access a variable with the wong size for the
> current thread seems rather fragile to me.

In many ways it seems similar to memory management.  It's the
programmer's responsibility to ensure that they don't access vectors
with the "wrong" size in the same way that it's their responsibility
not to dereference freed memory or access beyond the amount of memory
allocated.

And as I mentioned above, noone should be doing that anyway :-)
These types shouldn't live longer than a vector loop, with that loop
potentially calling functions that are easily identified as "vector
functions".

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 15:06           ` Richard Sandiford
@ 2018-10-17 15:25             ` Joseph Myers
  2018-10-18 13:33               ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Joseph Myers @ 2018-10-17 15:25 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Uecker, Martin, gcc-patches, jason, nathan

On Wed, 17 Oct 2018, Richard Sandiford wrote:

> Yeah, can't deny that if you look at it as a general-purpose extension.
> But that's not really what this is supposed to be.  It's fairly special
> purpose: there has to be some underlying variable-length/sizeless
> built-in type that you want to provide via a library.
> 
> What the extension allows is enough to support the intended use case,
> and it does that with no enforced overhead.

Part of my point is that there are various *other* possible cases of 
non-VLA-variable-size-type people have suggested in WG14 reflector 
discussions - so any set of concepts for such types ought to take into 
account more than just the SVE use case (even if other use cases need 
further concepts added on top of the ones needed for SVE).

> > Surely, the processor knows the size when it computes using these
> > types, so one could make it available using 'sizeof'.
> 
> The argument's similar here: we don't really need sizeof to be available
> for vector use because the library provides easy ways of getting
> vector-length-based constants.  Usually what you want to know is
> "how many elements of type X are there?", with bytes just being one
> of the available element sizes.

But if having sizeof available makes for a more natural language feature 
(one where a few places referencing VLAs need to change to reference a 
more general class of variable-size types, and a few constraints on VLAs 
and variably modified types need to be relaxed to allow what you want with 
these types), that may be a case for doing so, even if sizeof won't 
generally be used.

If the processor in fact knows the size, do you actually need to include 
it in the object to be able to provide it when sizeof is called?  (With 
undefined behavior still present if passing the object from a thread with 
one value of sizeof for that type to a thread with a different value of 
sizeof for that type, of course - the rule on VLA type compatibility would 
still need to be extended to apply to sizes of these types, and those they 
contain, recursively.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 14:39         ` Joseph Myers
@ 2018-10-18 12:01           ` Richard Sandiford
  2018-10-18 20:34             ` Joseph Myers
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-18 12:01 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Uecker, Martin, gcc-patches, jason, nathan

Joseph Myers <joseph@codesourcery.com> writes:
> On Wed, 17 Oct 2018, Richard Sandiford wrote:
>
>> > But as shown in the related discussions, there are other possible features 
>> > that might also involve non-VLA types whose size is not a compile-time 
>> > constant.  And so it's necessary to work with the people interested in 
>> > those features in order to clarify what the underlying concepts ought to 
>> > look like to support different such features.
>> 
>> Could you give pointers to the specific proposals/papers you mean?
>
> They're generally reflector discussions rather than written up as papers, 
> exploring the space of problems and solutions in various areas (including 
> bignums and runtime introspection of types).  I think the first message in 
> those discussions is number 15529 
> <http://www.open-std.org/jtc1/sc22/wg14/15529> and then relevant 
> discussions continue for much of the next 200 messages or so.

OK, thanks.  I've read from there to the latest message at the time
of writing (15720).  There seemed to be various ideas:

- a new int128_t, which started the discussion off.

- support for parameterised fixed-size integers like _Int(40), which
  seemed to be a C version of C++ template<int> and wouldn't need
  variable-length types.

- bignums that extend as necessary.  On that I agree with what you said in:

    <http://www.open-std.org/jtc1/sc22/wg14/15572>
    A bignum type, in the sense of one that grows its storage if you
    store a too-big number in it (as opposed to fixed-width int<N> where
    you can specify an arbitrary integer constant expression for N),
    cannot meet other requirements for C integer types such as being
    directly represented in binary - it has to, effectively, be a fixed
    size but contain a pointer to allocated storage (and then there are
    considerations of how such a type should handle errors for
    allocation failure).

  and Hans Boehm said in:

    <http://www.open-std.org/jtc1/sc22/wg14/15573>
    2) Provide an integral type that is reasonably efficient for small
    integers, but gracefully overflows to something along the lines of
    (1). A common way to do that in other languages is to represent
    e.g. 63-bit integers directly by adding a zero bit on the right.
    On overflow a more complex result is represented by e.g. a 64-bit
    aligned pointer with the low bit set to one. That way integer
    addition is just an add instruction followed by an overflow check in
    the normal case. Probably a better way to do integer arithmetic in
    many, maybe even most, cases. Especially since such integers need to
    be usable as array elements, I don't see how to avoid memory
    allocation under the covers, along the slow path.

  This IIRC is how LLVM's APInt is implemented.  It doesn't need
  variable-length types, and although it would need some kind of
  memory management support for C, it doesn't need any language
  changes at all for C++.

  It's also similar to what GCC does with auto_vec<T, N> and LLVM does
  with SmallVector: the types have embedded room for common cases and
  fall back to separately-allocated storage if the contents get too big.

  There was talk about having it as a true variable-length type in:

    <http://www.open-std.org/jtc1/sc22/wg14/15577>
    (2) is difficult because of the requirements for memory management and
    the necessity to deal with allocation failures.

    For avoiding integer overflow vulnerabilities, there is a variant of (2)
    which is not possible to implement in a library, where expressions are
    evaluated with a sufficient number of bits to obtain the mathematically
    correct result.  GNAT has implemented something in this direction
    (MINIMIZED and ELIMINATED):

    <https://gcc.gnu.org/onlinedocs/gnat_ugn/Management-of-Overflows-in-GNAT.html#Management-of-Overflows-in-GNAT>

    I think that for expressions which do not involve shifts by
    non-constants, it should be possible to determine the required storage
    at compile time, so it would avoid the memory allocation issue.  Unlike
    Ada, C doesn't have a power operator, so the storage requirements would
    grow with the size of the expression (still under the assumption that
    left shifts are excluded).

  But AIUI that was intended to be more special purpose, for
  intermediate results while evaluating an expression.  It solves
  the memory allocation issue because the (stack) memory used for
  evaluating the expression could be recovered after evaluation is
  complete.

  This approach wouldn't work if it was extended to an assignable bignum
  object type.  E.g. prohibiting left shifts wouldn't then help since:

     bignum x = ...;
     x <<= var; // invalid

  would be equivalent to:

     bignum x = ...;
     for (int i = 0; i < var; ++i)
       x += x; // valid

  Thus it would be easy to create what are effectively allocas of O(1<<var)
  bytes for some variable var.  And if the memory was always allocated on
  the stack, it would be hard to recover memory from discarded objects
  until the function returns.

  Hans went on to say:  

    I personally think that, especially in light of various integer overflow
    vulnerabilities, (2) would be really nice to have.

    I unfortunately haven't had time to follow the WG21 bignum discussion on
    this very closely. But my impression is that they're aiming to enable (2).

  So it sounds like bignums are being solved on the C++ side at least
  without having to add true variable-length types.

  FWIW, this corresponds to (3b) in the RFC, where a fixed-size type
  refers to separate storage where necessary.

- Type introspection for things like parsing format strings

  It sounded like the type descriptors would be fixed-sized types,
  a bit like a C version of std::type_info.

So I didn't see anything there that was really related, or anything that
relied on sizeof being variable (which as I say seems to be a very high
hurdle for C++).

Also, I thought opinion was turning/had turned against using what are
effectively unbounded allocas, so it would seem strange to spend a lot
of effort providing a more convenient wrapper for them.  (The SVE use
isn't unbounded because all sizeless objects are a X*VL+Y for constant
X and Y and bounded VL.)

>> ...and here is that any size changes come only from changes in the
>> implementation-defined built-in sizeless types.  The user can't define
>
> But then I think you still need to define in the standard edits something 
> of what the type-compatibility rules are.

I think it would look something like this (referring back to

    *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.*

in the RFC), not really in standardese yet:

    Each implementation-specific sizeless type may have a set of
    implementation-specific "configurations".  The configuration of
    such a type may change in implementation-defined ways at any given
    sequence point.

    The configuration of a sizeless structure is a tuple containing the
    configuration of each member.  Thus the configuration of a sizeless
    structure changes if and only if the configuration of one of its
    members changes.

    The configuration of an object of sizeless type T is the configuration
    of T at the point that the object is created.

And then borrowing slightly from your 6.7.6.2#6 reference:

    If an object of sizeless type T is accessed when T has a different
    configuration from the object, the behavior is undefined.

Is that the kind of thing you mean?

>> > Can these types be passed to variadic functions and named in va_arg?  
>> > Again, I don't see anything to say they can't.
>> 
>> Yes, this is allowed (and covered by the tests FWIW).
>
> How does that work with not knowing the size even at runtime?
>
> At least, this seems like another place where there would be special type 
> compatibility considerations that need to be applied between caller and 
> callee.

Yeah, it requires the caller and callee to agree on what the type
represents (in SVE terms, to have the same vector length).

>>     Except for bit-fields *and sizeless structures*, objects are
>>     composed of contiguous sequences of one or more bytes, the number,
>>     order, and encoding of which are either explicitly specified or
>>     implementation-defined.
>> 
>> TBH the possibility of a discontiguous representation was an early idea
>> that we've never actually used so far, so if that's a problem, we could
>> probably drop it.  It just seemed to be a natural extension of the
>> principle that the layout is completely implementation-defined.
>
> If you have discontiguous representations, I don't see how "->" on 
> structure pointers (or indeed unary "*") is supposed to work;

The idea was that there would be some indirection under the covers
where necessary.  E.g. the data pointed to the pointer may have a
hidden field that gives the offset or pointer to other storage.

The specific reason for thinking that might be useful was that it would
allow the compiler to divide a frame into "VL data" and "constant-sized
data".  A sizeless structure could be in one but refer to data in the
other.  But as I say, we never actually used that.

> disallowing discontiguous representations would seem to fit a lot more
> naturally with the C object model.

OK, I'll take out the discontiguous part.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-17 15:25             ` Joseph Myers
@ 2018-10-18 13:33               ` Richard Sandiford
  2018-10-18 20:06                 ` Uecker, Martin
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-18 13:33 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Uecker, Martin, gcc-patches, jason, nathan

Joseph Myers <joseph@codesourcery.com> writes:
> On Wed, 17 Oct 2018, Richard Sandiford wrote:
>> Yeah, can't deny that if you look at it as a general-purpose extension.
>> But that's not really what this is supposed to be.  It's fairly special
>> purpose: there has to be some underlying variable-length/sizeless
>> built-in type that you want to provide via a library.
>> 
>> What the extension allows is enough to support the intended use case,
>> and it does that with no enforced overhead.
>
> Part of my point is that there are various *other* possible cases of 
> non-VLA-variable-size-type people have suggested in WG14 reflector 
> discussions - so any set of concepts for such types ought to take into 
> account more than just the SVE use case (even if other use cases need 
> further concepts added on top of the ones needed for SVE).

[Answered this in the other thread -- sorry, took me a while to go
through the full discussion.]

>> > Surely, the processor knows the size when it computes using these
>> > types, so one could make it available using 'sizeof'.
>> 
>> The argument's similar here: we don't really need sizeof to be available
>> for vector use because the library provides easy ways of getting
>> vector-length-based constants.  Usually what you want to know is
>> "how many elements of type X are there?", with bytes just being one
>> of the available element sizes.
>
> But if having sizeof available makes for a more natural language feature 
> (one where a few places referencing VLAs need to change to reference a 
> more general class of variable-size types, and a few constraints on VLAs 
> and variably modified types need to be relaxed to allow what you want with 
> these types), that may be a case for doing so, even if sizeof won't 
> generally be used.

I agree that might be all that's needed in C.  But since C++ doesn't
even have VLAs yet (and since something less ambituous than VLAs was
rejected) the situation is very different there.

I think we'd need a compelling reason to make sizeof variable in C++.
The fact that it isn't going to be generally used for SVE anyway
would undercut that.

> If the processor in fact knows the size, do you actually need to include 
> it in the object to be able to provide it when sizeof is called?  (With 
> undefined behavior still present if passing the object from a thread with 
> one value of sizeof for that type to a thread with a different value of 
> sizeof for that type, of course - the rule on VLA type compatibility would 
> still need to be extended to apply to sizes of these types, and those they 
> contain, recursively.)

No, if we go the undefined behaviour route, we wouldn't need to store it.
This was just to answer Martin's suggestion that we could make sizeof(x)
do the right thing for a sizeless object x by storing the size with x.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [01/10] Expand COMPLETE_TYPE_P in obvious checks for null
  2018-10-15 14:33 ` [01/10] Expand COMPLETE_TYPE_P in obvious checks for null Richard Sandiford
@ 2018-10-18 19:32   ` Jeff Law
  0 siblings, 0 replies; 31+ messages in thread
From: Jeff Law @ 2018-10-18 19:32 UTC (permalink / raw)
  To: gcc-patches, joseph, jason, nathan, nd, richard.sandiford

On 10/15/18 8:31 AM, Richard Sandiford wrote:
> Some tests for COMPLETE_TYPE_P are just protecting against a null
> TYPE_SIZE or TYPE_SIZE_UNIT.  Rather than replace them with a new
> macro, it seemed clearer to write out the underlying test.
> 
> 2018-10-15  Richard Sandiford  <richard.sandiford@arm.com>
> 
> gcc/
> 	* calls.c (initialize_argument_information): Replace COMPLETE_TYPE_P
> 	with checks for null.
> 	* config/aarch64/aarch64.c (aapcs_vfp_sub_candidate): Likewise.
> 	* config/arm/arm.c (aapcs_vfp_sub_candidate): Likewise.
> 	* config/powerpcspe/powerpcspe.c (rs6000_aggregate_candidate):
> 	Likewise.
> 	* config/riscv/riscv.c (riscv_flatten_aggregate_field): Likewise.
> 	* config/rs6000/rs6000.c (rs6000_aggregate_candidate): Likewise.
> 	* expr.c (expand_assignment, safe_from_p): Likewise.
> 	(expand_expr_real_1): Likewise.
> 	* tree-data-ref.c (initialize_data_dependence_relation): Likewise.
> 	* tree-sra.c (maybe_add_sra_candidate): Likewise.
> 	(find_param_candidates): Likewise.
> 	* tree-ssa-alias.c (indirect_ref_may_alias_decl_p): Likewise.
> 	* tree-vrp.c (vrp_prop::check_mem_ref): Likewise.
> 
> gcc/lto/
> 	* lto-symtab.c (warn_type_compatibility_p): Likewise.
This seems largely independent of the larger changes you're trying to
make and could stand on its own.

I'm going to assume that you evaluated these instances of
COMPLETE_TYPE_P and determined that type completeness isn't really a
factor in the affected code.

OK if nobody objects by Monday.

jeff

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 13:33               ` Richard Sandiford
@ 2018-10-18 20:06                 ` Uecker, Martin
  2018-10-18 20:12                   ` Richard Sandiford
  2018-10-18 21:05                   ` Joseph Myers
  0 siblings, 2 replies; 31+ messages in thread
From: Uecker, Martin @ 2018-10-18 20:06 UTC (permalink / raw)
  To: richard.sandiford, joseph; +Cc: gcc-patches, jason, nathan


Hi Richard,

responding here to a couple of points.

For bignums and for a type-descibing type 'type'
there were proposals (including from me) to implement
these as variable-sized types which have some restrictions,
i.e. they cannot be stored in a struct/union.

Most of the restrictions for these types would be the same
as proposed for your sizeless types. 

Because all these types fall into the same overall class
of types which do not have a size known at compile
time, I would suggest to add this concept to the standard
and then define your vector types as a subclass which
may have additional restrictions (no sizeof) instead
of adding a very specific concept which only works for
your proposal.

Best,
Martin





Am Donnerstag, den 18.10.2018, 13:47 +0100 schrieb Richard Sandiford:
> Joseph Myers <joseph@codesourcery.com> writes:
> > On Wed, 17 Oct 2018, Richard Sandiford wrote:
> > > Yeah, can't deny that if you look at it as a general-purpose extension.
> > > But that's not really what this is supposed to be.  It's fairly special
> > > purpose: there has to be some underlying variable-length/sizeless
> > > built-in type that you want to provide via a library.
> > > 
> > > What the extension allows is enough to support the intended use case,
> > > and it does that with no enforced overhead.
> > 
> > Part of my point is that there are various *other* possible cases of 
> > non-VLA-variable-size-type people have suggested in WG14 reflector 
> > discussions - so any set of concepts for such types ought to take into 
> > account more than just the SVE use case (even if other use cases need 
> > further concepts added on top of the ones needed for SVE).
> 
> [Answered this in the other thread -- sorry, took me a while to go
> through the full discussion.]
> 
> > > > Surely, the processor knows the size when it computes using these
> > > > types, so one could make it available using 'sizeof'.
> > > 
> > > The argument's similar here: we don't really need sizeof to be available
> > > for vector use because the library provides easy ways of getting
> > > vector-length-based constants.  Usually what you want to know is
> > > "how many elements of type X are there?", with bytes just being one
> > > of the available element sizes.
> > 
> > But if having sizeof available makes for a more natural language feature 
> > (one where a few places referencing VLAs need to change to reference a 
> > more general class of variable-size types, and a few constraints on VLAs 
> > and variably modified types need to be relaxed to allow what you want with 
> > these types), that may be a case for doing so, even if sizeof won't 
> > generally be used.
> 
> I agree that might be all that's needed in C.  But since C++ doesn't
> even have VLAs yet (and since something less ambituous than VLAs was
> rejected) the situation is very different there.
> 
> I think we'd need a compelling reason to make sizeof variable in C++.
> The fact that it isn't going to be generally used for SVE anyway
> would undercut that.
> 
> > If the processor in fact knows the size, do you actually need to include 
> > it in the object to be able to provide it when sizeof is called?  (With 
> > undefined behavior still present if passing the object from a thread with 
> > one value of sizeof for that type to a thread with a different value of 
> > sizeof for that type, of course - the rule on VLA type compatibility would 
> > still need to be extended to apply to sizes of these types, and those they 
> > contain, recursively.)
> 
> No, if we go the undefined behaviour route, we wouldn't need to store it.
> This was just to answer Martin's suggestion that we could make sizeof(x)
> do the right thing for a sizeless object x by storing the size with x.
> 
> Thanks,
> Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 20:06                 ` Uecker, Martin
@ 2018-10-18 20:12                   ` Richard Sandiford
  2018-10-18 21:09                     ` Uecker, Martin
  2018-10-18 21:05                   ` Joseph Myers
  1 sibling, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-18 20:12 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: joseph, gcc-patches, jason, nathan

"Uecker, Martin" <Martin.Uecker@med.uni-goettingen.de> writes:
> Hi Richard,
>
> responding here to a couple of points.
>
> For bignums and for a type-descibing type 'type'
> there were proposals (including from me) to implement
> these as variable-sized types which have some restrictions,
> i.e. they cannot be stored in a struct/union.

But do you mean variable-sized types in the sense that they
are completely self-contained and don't refer to separate storage?
I.e. the moral equivalent of:

  1: struct { int size; int contents[size]; };

rather than either:

  2: struct { int size; int *contents; };

or:

  3: union {
       // embedded storage up to N bits (N constant)
       // description of separately-allocated storage (for >N bits)
     };
    
?  If so, how would that work with the example I gave in the earlier
message:

    bignum x = ...;
    for (int i = 0; i < var; ++i)
      x += x;

Each time the addition result grows beyond the original size of x,
I assume you'd need to allocate a new stack bignum for the new size,
which would result in a series of ever-increasing allocas.  Won't that
soon blow the stack?

Option 3 (as for LLVM's APInt) seems far less surprising, and can
be made efficient for a chosen N.  What makes it difficult for C
isn't the lack of general variable-length types but the lack of
user-defined contructor, destructor, copy and move operations.

Thanks,
Richard

> Most of the restrictions for these types would be the same
> as proposed for your sizeless types. 
>
> Because all these types fall into the same overall class
> of types which do not have a size known at compile
> time, I would suggest to add this concept to the standard
> and then define your vector types as a subclass which
> may have additional restrictions (no sizeof) instead
> of adding a very specific concept which only works for
> your proposal.
>
> Best,
> Martin
>
>
>
>
>
> Am Donnerstag, den 18.10.2018, 13:47 +0100 schrieb Richard Sandiford:
>> Joseph Myers <joseph@codesourcery.com> writes:
>> > On Wed, 17 Oct 2018, Richard Sandiford wrote:
>> > > Yeah, can't deny that if you look at it as a general-purpose extension.
>> > > But that's not really what this is supposed to be.  It's fairly special
>> > > purpose: there has to be some underlying variable-length/sizeless
>> > > built-in type that you want to provide via a library.
>> > > 
>> > > What the extension allows is enough to support the intended use case,
>> > > and it does that with no enforced overhead.
>> > 
>> > Part of my point is that there are various *other* possible cases of 
>> > non-VLA-variable-size-type people have suggested in WG14 reflector 
>> > discussions - so any set of concepts for such types ought to take into 
>> > account more than just the SVE use case (even if other use cases need 
>> > further concepts added on top of the ones needed for SVE).
>> 
>> [Answered this in the other thread -- sorry, took me a while to go
>> through the full discussion.]
>> 
>> > > > Surely, the processor knows the size when it computes using these
>> > > > types, so one could make it available using 'sizeof'.
>> > > 
>> > > The argument's similar here: we don't really need sizeof to be available
>> > > for vector use because the library provides easy ways of getting
>> > > vector-length-based constants.  Usually what you want to know is
>> > > "how many elements of type X are there?", with bytes just being one
>> > > of the available element sizes.
>> > 
>> > But if having sizeof available makes for a more natural language feature 
>> > (one where a few places referencing VLAs need to change to reference a 
>> > more general class of variable-size types, and a few constraints on VLAs 
>> > and variably modified types need to be relaxed to allow what you want with 
>> > these types), that may be a case for doing so, even if sizeof won't 
>> > generally be used.
>> 
>> I agree that might be all that's needed in C.  But since C++ doesn't
>> even have VLAs yet (and since something less ambituous than VLAs was
>> rejected) the situation is very different there.
>> 
>> I think we'd need a compelling reason to make sizeof variable in C++.
>> The fact that it isn't going to be generally used for SVE anyway
>> would undercut that.
>> 
>> > If the processor in fact knows the size, do you actually need to include 
>> > it in the object to be able to provide it when sizeof is called?  (With 
>> > undefined behavior still present if passing the object from a thread with 
>> > one value of sizeof for that type to a thread with a different value of 
>> > sizeof for that type, of course - the rule on VLA type compatibility would 
>> > still need to be extended to apply to sizes of these types, and those they 
>> > contain, recursively.)
>> 
>> No, if we go the undefined behaviour route, we wouldn't need to store it.
>> This was just to answer Martin's suggestion that we could make sizeof(x)
>> do the right thing for a sizeless object x by storing the size with x.
>> 
>> Thanks,
>> Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 12:01           ` Richard Sandiford
@ 2018-10-18 20:34             ` Joseph Myers
  2018-10-19 12:34               ` Richard Sandiford
  0 siblings, 1 reply; 31+ messages in thread
From: Joseph Myers @ 2018-10-18 20:34 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Uecker, Martin, gcc-patches, jason, nathan

On Thu, 18 Oct 2018, Richard Sandiford wrote:

> - Type introspection for things like parsing format strings
> 
>   It sounded like the type descriptors would be fixed-sized types,
>   a bit like a C version of std::type_info.

It wasn't clear if people might also want to e.g. extract a list of all 
members of a structure type from such an object (which of course could 
either involve variable-sized data, or fixed-size data pointing to arrays, 
or something else along those lines).

> So I didn't see anything there that was really related, or anything that
> relied on sizeof being variable (which as I say seems to be a very high
> hurdle for C++).

The references you gave regarding the removal of one version of VLAs from 
C++ didn't seem to make clear whether there were supposed to be general 
issues with variable-size types fitting in the overall C++ object model, 
or whether the concerns were more specific to things in the particular 
proposal - but in either case, the SVE proposals would need to be compared 
to the actual specific concerns.

Anyway, the correct model in C++ need not be the same as the correct model 
in C.  For example, for decimal floating point, C++ chose a class-based 
model whereas C chose _Decimal* keywords (and then there's some compiler 
magic to use appropriate ABIs for std::decimal types, I think).

If you were implementing the SVE API for C++ for non-SVE hardware, you 
might have a class-based implementation where the class internally 
contains a pointer to underlying storage and does allocation / 
deallocation, for example - sizeof would give some fixed small size to the 
objects with that class type, but e.g. copying them with memcpy would not 
work correctly (and would be diagnosed with -Wclass-memaccess).  Is there 
something wrong with a model in C++ where these types have some fixed 
small sizeof (which carries through to sizeof for containing types), but 
where different ABIs are used for them, and where much the same raw memory 
operations on them are disallowed as would be disallowed for a class-based 
implementation?  (Whether implemented entirely in the compiler or through 
some combination of the compiler and class implementations in a header - 
though with the latter you might still need some new language feature, 
albeit only for use within the header rather than more generally.)

Even if that model doesn't work for some reason, it doesn't mean the only 
alternatives for C++ are something like VLAs or a new concept of sizeless 
types for C++ - but I don't have the C++ expertise to judge what other 
options for interfacing to SVE might fit best into the C++ language.

> I think it would look something like this (referring back to
> 
>     *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.*
> 
> in the RFC), not really in standardese yet:
> 
>     Each implementation-specific sizeless type may have a set of
>     implementation-specific "configurations".  The configuration of
>     such a type may change in implementation-defined ways at any given
>     sequence point.
> 
>     The configuration of a sizeless structure is a tuple containing the
>     configuration of each member.  Thus the configuration of a sizeless
>     structure changes if and only if the configuration of one of its
>     members changes.
> 
>     The configuration of an object of sizeless type T is the configuration
>     of T at the point that the object is created.
> 
> And then borrowing slightly from your 6.7.6.2#6 reference:
> 
>     If an object of sizeless type T is accessed when T has a different
>     configuration from the object, the behavior is undefined.
> 
> Is that the kind of thing you mean?

Yes.  But I wonder if it would be better to disallow such changing of 
configurations, so that all code in a program always uses the same 
configuration as far as the standard is concerned, so that there is indeed 
a size for a given vector type that's constant throughout the execution of 
a program (which would be used by calls to sizeof on such types), and so 
that communicating with a thread using a different configuration is just 
as much outside the scope of the defined language as processes using 
different ABIs communicating is today.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 20:06                 ` Uecker, Martin
  2018-10-18 20:12                   ` Richard Sandiford
@ 2018-10-18 21:05                   ` Joseph Myers
  1 sibling, 0 replies; 31+ messages in thread
From: Joseph Myers @ 2018-10-18 21:05 UTC (permalink / raw)
  To: Uecker, Martin; +Cc: richard.sandiford, gcc-patches, jason, nathan

On Thu, 18 Oct 2018, Uecker, Martin wrote:

> Most of the restrictions for these types would be the same
> as proposed for your sizeless types. 
> 
> Because all these types fall into the same overall class
> of types which do not have a size known at compile
> time, I would suggest to add this concept to the standard
> and then define your vector types as a subclass which
> may have additional restrictions (no sizeof) instead
> of adding a very specific concept which only works for
> your proposal.

And an underlying point here is:

Various people are exploring various ideas for C language and library 
features that might involve extending the kinds of types present in C.  
Maybe some of the ideas will turn out to be fundamentally flawed; maybe 
some will work with existing kinds of types rather than needing new kinds 
of variable-sized types.  But since all those ideas are currently under 
discussion in WG14, the SVE issues should be brought into the exploration 
process taking place there, with a view to getting a better-defined set of 
concepts for such types out of that process than from considering just one 
proposal for concepts for one set of requirements in the context of one 
implementation.

Once that discussion has resulted in a more generally applicable set of 
concepts, experience in implementing that set of concepts - likely various 
different people implementing them, in different C implementations, with a 
view to the different use cases they are exploring - could help inform any 
standardization of such features.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 20:12                   ` Richard Sandiford
@ 2018-10-18 21:09                     ` Uecker, Martin
  0 siblings, 0 replies; 31+ messages in thread
From: Uecker, Martin @ 2018-10-18 21:09 UTC (permalink / raw)
  To: richard.sandiford; +Cc: gcc-patches, jason, nathan, joseph

Am Donnerstag, den 18.10.2018, 20:53 +0100 schrieb Richard Sandiford:
> "Uecker, Martin" <Martin.Uecker@med.uni-goettingen.de> writes:
> > Hi Richard,
> > 
> > responding here to a couple of points.
> > 
> > For bignums and for a type-descibing type 'type'
> > there were proposals (including from me) to implement
> > these as variable-sized types which have some restrictions,
> > i.e. they cannot be stored in a struct/union.
> 
> But do you mean variable-sized types in the sense that they
> are completely self-contained and don't refer to separate storage?
> I.e. the moral equivalent of:
> 
>   1: struct { int size; int contents[size]; };
> 
> rather than either:
> 
>   2: struct { int size; int *contents; };

I was thinking about 1 not 2. But I would leave this to the
implementation. If it can unwind the stack and free
all allocated storage automatically whenever this is
necessary, it could also allocate it somewhere else.
Not that this would offer any real advantage...

In both cases the only real problem is when storing
these in structs. So this should simply be forbidden
as it is for VLAs.

> or:
> 
>   3: union {
>        // embedded storage up to N bits (N constant)
>        // description of separately-allocated storage (for >N bits)
>      };

This is essentially an optimized version of 2.
    
> ?  If so, how would that work with the example I gave in the earlier
> message:
> 
>     bignum x = ...;
>     for (int i = 0; i < var; ++i)
>       x += x;
> 
> Each time the addition result grows beyond the original size of x,
> I assume you'd need to allocate a new stack bignum for the new size,
> which would result in a series of ever-increasing allocas.  Won't that
> soon blow the stack?

That depends on the final size of x relative to the size of the stack.
But this is no different to:

for (int i = 0; i < var; ++)
{
   int x[i];
}

or to a recursive function. There are many ways to exhaust the
stack. It is also possible to exhaust other kinds of resources.
I don't really see the problem.

> Option 3 (as for LLVM's APInt) seems far less surprising, and can
> be made efficient for a chosen N.

Far less surprising in what sense?

>  What makes it difficult for C
> isn't the lack of general variable-length types but the lack of
> user-defined contructor, destructor, copy and move operations.

C already has generic variable-length types (VLAs). So yes, 
this is not what makes it difficult.

Yes, descructors would be needed to make it possible to store
these types in struct without memory leakage. But the destructors
don't need to be user defined, it could be a special purpose
destructor which only frees the special type.

But it doesn't really fit in the way C works and I kind of like
that C doesn't do anything behind my back.

Best,
Martin


> Thanks,
> Richard
> 
> > Most of the restrictions for these types would be the same
> > as proposed for your sizeless types. 
> > 
> > Because all these types fall into the same overall class
> > of types which do not have a size known at compile
> > time, I would suggest to add this concept to the standard
> > and then define your vector types as a subclass which
> > may have additional restrictions (no sizeof) instead
> > of adding a very specific concept which only works for
> > your proposal.
> > 
> > Best,
> > Martin
> > 
> > 
> > 
> > 
> > 
> > Am Donnerstag, den 18.10.2018, 13:47 +0100 schrieb Richard Sandiford:
> > > Joseph Myers <joseph@codesourcery.com> writes:
> > > > On Wed, 17 Oct 2018, Richard Sandiford wrote:
> > > > > Yeah, can't deny that if you look at it as a general-purpose extension.
> > > > > But that's not really what this is supposed to be.  It's fairly special
> > > > > purpose: there has to be some underlying variable-length/sizeless
> > > > > built-in type that you want to provide via a library.
> > > > > 
> > > > > What the extension allows is enough to support the intended use case,
> > > > > and it does that with no enforced overhead.
> > > > 
> > > > Part of my point is that there are various *other* possible cases of 
> > > > non-VLA-variable-size-type people have suggested in WG14 reflector 
> > > > discussions - so any set of concepts for such types ought to take into 
> > > > account more than just the SVE use case (even if other use cases need 
> > > > further concepts added on top of the ones needed for SVE).
> > > 
> > > [Answered this in the other thread -- sorry, took me a while to go
> > > through the full discussion.]
> > > 
> > > > > > Surely, the processor knows the size when it computes using these
> > > > > > types, so one could make it available using 'sizeof'.
> > > > > 
> > > > > The argument's similar here: we don't really need sizeof to be available
> > > > > for vector use because the library provides easy ways of getting
> > > > > vector-length-based constants.  Usually what you want to know is
> > > > > "how many elements of type X are there?", with bytes just being one
> > > > > of the available element sizes.
> > > > 
> > > > But if having sizeof available makes for a more natural language feature 
> > > > (one where a few places referencing VLAs need to change to reference a 
> > > > more general class of variable-size types, and a few constraints on VLAs 
> > > > and variably modified types need to be relaxed to allow what you want with 
> > > > these types), that may be a case for doing so, even if sizeof won't 
> > > > generally be used.
> > > 
> > > I agree that might be all that's needed in C.  But since C++ doesn't
> > > even have VLAs yet (and since something less ambituous than VLAs was
> > > rejected) the situation is very different there.
> > > 
> > > I think we'd need a compelling reason to make sizeof variable in C++.
> > > The fact that it isn't going to be generally used for SVE anyway
> > > would undercut that.
> > > 
> > > > If the processor in fact knows the size, do you actually need to include 
> > > > it in the object to be able to provide it when sizeof is called?  (With 
> > > > undefined behavior still present if passing the object from a thread with 
> > > > one value of sizeof for that type to a thread with a different value of 
> > > > sizeof for that type, of course - the rule on VLA type compatibility would 
> > > > still need to be extended to apply to sizes of these types, and those they 
> > > > contain, recursively.)
> > > 
> > > No, if we go the undefined behaviour route, we wouldn't need to store it.
> > > This was just to answer Martin's suggestion that we could make sizeof(x)
> > > do the right thing for a sizeless object x by storing the size with x.
> > > 
> > > Thanks,
> > > Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-18 20:34             ` Joseph Myers
@ 2018-10-19 12:34               ` Richard Sandiford
  2018-10-19 13:42                 ` Joseph Myers
  0 siblings, 1 reply; 31+ messages in thread
From: Richard Sandiford @ 2018-10-19 12:34 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Uecker, Martin, gcc-patches, jason, nathan

Joseph Myers <joseph@codesourcery.com> writes:
> On Thu, 18 Oct 2018, Richard Sandiford wrote:
>> - Type introspection for things like parsing format strings
>> 
>>   It sounded like the type descriptors would be fixed-sized types,
>>   a bit like a C version of std::type_info.
>
> It wasn't clear if people might also want to e.g. extract a list of all 
> members of a structure type from such an object (which of course could 
> either involve variable-sized data, or fixed-size data pointing to arrays, 
> or something else along those lines).

OK.  But wouldn't that basically be a tree structure?  Or a flexible
array if flattened?  It doesn't sound like it would need changes to
the type system.  We can already describe this kind of thing with tree
types in GCC.  (The memory needed to represent the data could of course
be allocated using a single block of stack if that's what's wanted.)

>> So I didn't see anything there that was really related, or anything that
>> relied on sizeof being variable (which as I say seems to be a very high
>> hurdle for C++).
>
> The references you gave regarding the removal of one version of VLAs from 
> C++ didn't seem to make clear whether there were supposed to be general 
> issues with variable-size types fitting in the overall C++ object model, 
> or whether the concerns were more specific to things in the particular 
> proposal - but in either case, the SVE proposals would need to be compared 
> to the actual specific concerns.

But this is also one of my concerns about moving this discussing to the
WG14 list.  It doesn't seem to be publicly readable, and I only knew
about the bignum discussion because you gave me a direct link to the
first article in the thread.  I had to read the rest by wgetting the
individual messages.  So any objections raised there would presumably
be shrouded in mystery to most people, and wouldn't e.g. show up in a
web search.

If we move it to a different forum, I'd rather it be a public one that
would treat C and C++ equally.  But maybe such a thing doesn't exist. :-)

> Anyway, the correct model in C++ need not be the same as the correct model 
> in C.  For example, for decimal floating point, C++ chose a class-based 
> model whereas C chose _Decimal* keywords (and then there's some compiler 
> magic to use appropriate ABIs for std::decimal types, I think).
>
> If you were implementing the SVE API for C++ for non-SVE hardware, you 
> might have a class-based implementation where the class internally 
> contains a pointer to underlying storage and does allocation / 
> deallocation, for example - sizeof would give some fixed small size to the 
> objects with that class type, but e.g. copying them with memcpy would not 
> work correctly (and would be diagnosed with -Wclass-memaccess).

One important point here is that the SVE API isn't a new API whose
primary target happens to be SVE.  It's an API whose *only* target
is SVE.  Anyone wanting to write vector code that runs on non-SVE
hardware should use something that's designed to be cross-platform,
(e.g. P0214 or whatever).  They certainly shouldn't be using this.

Like other vector intrinsics, the SVE ACLE is supposed to be the last
line of defence before resorting to asm, and isn't designed to be any
more portable than asm would be.

> Is there something wrong with a model in C++ where these types have
> some fixed small sizeof (which carries through to sizeof for
> containing types), but where different ABIs are used for them, and
> where much the same raw memory operations on them are disallowed as
> would be disallowed for a class-based implementation?  (Whether
> implemented entirely in the compiler or through some combination of
> the compiler and class implementations in a header - though with the
> latter you might still need some new language feature, albeit only for
> use within the header rather than more generally.)

Having different ABIs would defeat the primary purpose of the extension,
which is to provide access to the single-vector SVE ABI types in C and C++.
We want types that in both C and C++ represent the contents of SVE vector
and predicate registers.  E.g.:

  svfloat64_t vector_sin(svbool_t pg, svfloat64_t vx)

has to map pg to a predicate register (P0), vx to a vector register (Z0)
and return the result in a vector register (Z0), in both C and C++.

The main objection to the details of the sizeless type proposal seems
to be that sizeof was too useful for us to make it invalid.  But if
sizeof has different values for C and C++, wouldn't that defeat the
point?  Users would be forced to use the SVE vector length functions
after all.  Also, for:

  void (*update_vector)(svfloat64_t *px);

how would the caller of update_vector know whether the target
function is using the C or the C++ representation of svfloat64_t
when accessing *px?

> Even if that model doesn't work for some reason, it doesn't mean the only 
> alternatives for C++ are something like VLAs or a new concept of sizeless 
> types for C++ - but I don't have the C++ expertise to judge what other 
> options for interfacing to SVE might fit best into the C++ language.

This is one of the reasons I was raising this as a joint RFC about what
would be acceptable in both the C and C++ frontends (treating it as
either a GNU extension or a target extension for now).

The sizeless type proposal keeps the new built-in vector types in
the common subset of C and C++ by "opting out of" (among other things)
features in one language that would make them problematic in the other.

>> I think it would look something like this (referring back to
>> 
>>     *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.*
>> 
>> in the RFC), not really in standardese yet:
>> 
>>     Each implementation-specific sizeless type may have a set of
>>     implementation-specific "configurations".  The configuration of
>>     such a type may change in implementation-defined ways at any given
>>     sequence point.
>> 
>>     The configuration of a sizeless structure is a tuple containing the
>>     configuration of each member.  Thus the configuration of a sizeless
>>     structure changes if and only if the configuration of one of its
>>     members changes.
>> 
>>     The configuration of an object of sizeless type T is the configuration
>>     of T at the point that the object is created.
>> 
>> And then borrowing slightly from your 6.7.6.2#6 reference:
>> 
>>     If an object of sizeless type T is accessed when T has a different
>>     configuration from the object, the behavior is undefined.
>> 
>> Is that the kind of thing you mean?
>
> Yes.  But I wonder if it would be better to disallow such changing of 
> configurations, so that all code in a program always uses the same 
> configuration as far as the standard is concerned, so that there is indeed 
> a size for a given vector type that's constant throughout the execution of 
> a program (which would be used by calls to sizeof on such types), and so 
> that communicating with a thread using a different configuration is just 
> as much outside the scope of the defined language as processes using 
> different ABIs communicating is today.

The problem isn't just communicating with different threads though.
If the program changes the vector length and consistently uses objects
created with that new vector length, the size of the object would still
be different from the size of the real payload.  We would have to resort
to the type having implementation-defined amounts of padding, with the
amount of padding varying at sequence points in the same way as above.
(This would effectively be (3a) from the RFC FWIW.)

The reason for adding the above was to describe the ways in which
accessing an object with the "wrong length" would invoke undefined
behaviour.  If we say that there never is a wrong length (and thus
no undefined behaviour from using the wrong length) we'd just end
up with a legal fiction.  It'd seem better not to add it at all.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [00/10][RFC] Splitting the C and C++ concept of "complete type"
  2018-10-19 12:34               ` Richard Sandiford
@ 2018-10-19 13:42                 ` Joseph Myers
  0 siblings, 0 replies; 31+ messages in thread
From: Joseph Myers @ 2018-10-19 13:42 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Uecker, Martin, gcc-patches, jason, nathan

On Fri, 19 Oct 2018, Richard Sandiford wrote:

> Joseph Myers <joseph@codesourcery.com> writes:
> > On Thu, 18 Oct 2018, Richard Sandiford wrote:
> >> - Type introspection for things like parsing format strings
> >> 
> >>   It sounded like the type descriptors would be fixed-sized types,
> >>   a bit like a C version of std::type_info.
> >
> > It wasn't clear if people might also want to e.g. extract a list of all 
> > members of a structure type from such an object (which of course could 
> > either involve variable-sized data, or fixed-size data pointing to arrays, 
> > or something else along those lines).
> 
> OK.  But wouldn't that basically be a tree structure?  Or a flexible
> array if flattened?  It doesn't sound like it would need changes to

I don't know (but you mention flexible arrays, and initializers for 
flexible array members, where the size of the object ends up bigger than 
sizeof its type, are also a GNU extension).  Raise that question in the 
WG14 discussion; I'm not the right person to answer questions around 
everyone else's ideas for extensions to the C type system.  As far as I'm 
concerned, this is all a preliminary exploration of ideas that might or 
might not end up involving type system additions, and WG14 is a much 
better place for that than separate single-implementation discussions - 
the point should be to float and explore possible ideas in this space, and 
their benefits and disadvantages, rather than pushing too early for one 
particular approach.  And given how much C++ tends to use class-based 
interfaces where C uses built-in types (complex numbers, decimal floating 
point, ...), I definitely do not want to start from an assumption that the 
right interface or language concepts for this in C++ should look like 
those in C.

For me, thinking of SVE types as something like VLAs but passed by value 
seems a more natural model in C than having them sizeless - but if they 
are sizeless, that pushes them closer to other ideas for types that might 
also be sizeless (and if those other use cases are indeed best specified 
using sizeless types, that provides more justification for using sizeless 
types for SVE).

> > Is there something wrong with a model in C++ where these types have
> > some fixed small sizeof (which carries through to sizeof for
> > containing types), but where different ABIs are used for them, and
> > where much the same raw memory operations on them are disallowed as
> > would be disallowed for a class-based implementation?  (Whether
> > implemented entirely in the compiler or through some combination of
> > the compiler and class implementations in a header - though with the
> > latter you might still need some new language feature, albeit only for
> > use within the header rather than more generally.)
> 
> Having different ABIs would defeat the primary purpose of the extension,
> which is to provide access to the single-vector SVE ABI types in C and C++.

My suggestion is that the ABI for C++ would be different from that 
resulting for a class-based implementation using purely standard C++ (the 
difference being to make it the same as the SVE C API - as with the 
decimal floating-point classes).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2018-10-19 13:14 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-15 14:32 [00/10][RFC] Splitting the C and C++ concept of "complete type" Richard Sandiford
2018-10-15 14:33 ` [01/10] Expand COMPLETE_TYPE_P in obvious checks for null Richard Sandiford
2018-10-18 19:32   ` Jeff Law
2018-10-15 14:34 ` [03/10] Move COMPLETE_OR_VOID_TYPE_P to the C and C++ frontends Richard Sandiford
2018-10-15 14:34 ` [02/10] Replace most uses of COMPLETE_TYPE_P outside the frontends Richard Sandiford
2018-10-15 14:35 ` [04/10] Move COMPLETE_OR_UNBOUND_ARRAY_TYPE_P to the C and C++ frontends Richard Sandiford
2018-10-15 14:36 ` [05/10] Move complete_or_array_type_p " Richard Sandiford
2018-10-15 14:36 ` [06/10] Move COMPLETE_TYPE_P " Richard Sandiford
2018-10-15 14:37 ` [07/10] Use COMPLETE_TYPE_P instead of TYPE_SIZE Richard Sandiford
2018-10-15 14:38 ` [08/10] Add a TYPE_SIZELESS_P property to types Richard Sandiford
2018-10-15 14:50 ` [09/10] C support for sizeless types Richard Sandiford
2018-10-15 15:01 ` [10/10] C++ " Richard Sandiford
2018-10-15 15:14 ` [00/10][RFC] Splitting the C and C++ concept of "complete type" Joseph Myers
2018-10-15 18:57 ` Uecker, Martin
2018-10-16  8:51   ` Richard Biener
2018-10-16 12:55   ` Richard Sandiford
2018-10-16 23:07     ` Joseph Myers
2018-10-17 12:54       ` Richard Sandiford
2018-10-17 14:39         ` Joseph Myers
2018-10-18 12:01           ` Richard Sandiford
2018-10-18 20:34             ` Joseph Myers
2018-10-19 12:34               ` Richard Sandiford
2018-10-19 13:42                 ` Joseph Myers
2018-10-17 14:39         ` Uecker, Martin
2018-10-17 15:06           ` Richard Sandiford
2018-10-17 15:25             ` Joseph Myers
2018-10-18 13:33               ` Richard Sandiford
2018-10-18 20:06                 ` Uecker, Martin
2018-10-18 20:12                   ` Richard Sandiford
2018-10-18 21:09                     ` Uecker, Martin
2018-10-18 21:05                   ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).