rsandifo-arm created this revision.
Herald added subscribers: cfe-commits, jfb, kristof.beyls, javed.absar.
Herald added a reviewer: rengolin.
Herald added a project: clang.

This patch adds the concept of "sizeless" types to C and C++.
The documentation part of the patch describes the extension in
detail and explains the rationale.  The patch is a prerequisite
for adding AArch64 SVE intrinsic functions.

The main change is to make several checks use the new distinction
between "definite" and "indefinite" types instead of the usual
distinction between "complete" and "incomplete" types.  There are
also a couple of places that check specifically for sizeless types.

The patch doesn't reword the diagnostics to talk about indefinite
types rather than incomplete types since (a) "indefinite" won't
mean much to most users and (b) the patch shouldn't affect the user
experience for non-AArch64 targets.

The FIXMEs about inline asms are resolved by later SVE patches.

The change to DiagnoseForRangeVariableCopies is for consistency.
As the tests show, DiagnoseForRangeReferenceVariableCopies can apply
to sizeless types, but at the moment DiagnoseForRangeConstVariableCopies
(which is the only part affected by the completeness check) doesn't
warn for PODs, and so doesn't warn for sizeless types either.


Repository:
  rC Clang

https://reviews.llvm.org/D62962

Files:
  docs/SizelessTypes.rst
  include/clang/AST/Expr.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Initialization.h
  include/clang/Sema/Sema.h
  lib/AST/ASTContext.cpp
  lib/AST/ExprClassification.cpp
  lib/AST/Type.cpp
  lib/Sema/SemaChecking.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclCXX.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaFixItUtils.cpp
  lib/Sema/SemaInit.cpp
  lib/Sema/SemaLambda.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaPseudoObject.cpp
  lib/Sema/SemaStmt.cpp
  lib/Sema/SemaStmtAsm.cpp
  lib/Sema/SemaType.cpp
  test/Sema/sizeless-1.c
  test/SemaCXX/sizeless-1.cpp

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

Reply via email to