aaron.ballman updated this revision to Diff 461583.
aaron.ballman added a comment.

Updated the C status page accordingly.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D134286/new/

https://reviews.llvm.org/D134286

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/AST/Type.h
  clang/include/clang/AST/TypeProperties.td
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/Specifiers.h
  clang/include/clang/Basic/TokenKinds.def
  clang/include/clang/Sema/DeclSpec.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/ASTImporter.cpp
  clang/lib/AST/Type.cpp
  clang/lib/AST/TypePrinter.cpp
  clang/lib/Lex/Preprocessor.cpp
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Parse/ParseExpr.cpp
  clang/lib/Sema/DeclSpec.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaTemplateVariadic.cpp
  clang/lib/Sema/SemaType.cpp
  clang/lib/Sema/TreeTransform.h
  clang/test/C/C2x/n2927.c
  clang/test/C/C2x/n2927_2.c
  clang/test/C/C2x/n2930.c
  clang/test/Lexer/keywords_test.c
  clang/test/Parser/c2x-typeof-ext-warns.c
  clang/test/Parser/c2x-typeof.c
  clang/test/Sema/c2x-typeof.c
  clang/www/c_status.html

Index: clang/www/c_status.html
===================================================================
--- clang/www/c_status.html
+++ clang/www/c_status.html
@@ -1092,17 +1092,11 @@
     </tr>
       <tr> <!-- Feb 2022 -->
         <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm";>N2927</a></td>
-        <td class="partial" align="center">
-          <details><summary>Partial</summary>
-            Clang supports <code>typeof</code> in GNU standards mode, but its
-            compatibility with this proposal is unknown. Also, Clang does not yet
-            support remove_quals.
-          </details>
-        </td>
+        <td class="unreleased" align="center">Clang 16</td>
       </tr>
       <tr> <!-- Jul 2022 -->
         <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2930.pdf";>N2930</a></td>
-        <td class="none" align="center">No</td>
+        <td class="unreleased" align="center">Clang 16</td>
       </tr>
     <tr>
       <td>Type annex tgmath narrowing macros with integer args v2</td>
Index: clang/test/Sema/c2x-typeof.c
===================================================================
--- /dev/null
+++ clang/test/Sema/c2x-typeof.c
@@ -0,0 +1,94 @@
+// RUN: %clang_cc1 -verify -std=c2x %s
+
+// Demonstrate that we get the correct type information. Do this by leaning
+// heavily on redeclarations needing to use the same type for both decls.
+extern int i;
+extern typeof(i) i;
+extern typeof_unqual(i) i;
+
+extern const int j;
+extern typeof(j) j;
+
+extern const int n;         // expected-note 2 {{previous declaration is here}}
+extern typeof(i) n;         // expected-error {{redeclaration of 'n' with a different type: 'typeof (i)' (aka 'int') vs 'const int'}}
+extern typeof_unqual(n) n;  // expected-error {{redeclaration of 'n' with a different type: 'typeof_unqual (n)' (aka 'int') vs 'const int'}}
+
+// Ensure we get a redeclaration error here for the types not matching.
+extern typeof(j) k;        // expected-note {{previous declaration is here}}
+extern typeof_unqual(j) k; // expected-error {{redeclaration of 'k' with a different type: 'typeof_unqual (j)' (aka 'int') vs 'typeof (j)' (aka 'const int')}}
+
+// Make sure the type-form of the operator also works.
+extern typeof(int) l;
+extern typeof_unqual(const int) l;
+
+extern typeof(const int) m;        // expected-note {{previous declaration is here}}
+extern typeof_unqual(const int) m; // expected-error {{redeclaration of 'm' with a different type: 'typeof_unqual(const int)' (aka 'int') vs 'typeof(const int)' (aka 'const int')}}
+
+// Show that we can use an incomplete type which is then completed later.
+extern typeof(struct T) *o;
+struct T { int a; } t;
+extern typeof(struct T) *o;
+extern typeof(t) *o;
+extern typeof(&t) o;
+extern typeof_unqual(volatile struct T) *o;
+extern typeof_unqual(t) *o;
+extern typeof_unqual(&t) o;
+
+// Show that we properly strip the _Atomic qualifier.
+extern _Atomic int i2;
+extern _Atomic(int) i2;
+extern typeof(i2) i2;        // expected-note {{previous declaration is here}}
+extern typeof_unqual(i2) i2; // expected-error {{redeclaration of 'i2' with a different type: 'typeof_unqual (i2)' (aka 'int') vs 'typeof (i2)' (aka '_Atomic(int)')}}
+
+// We cannot take the type of a bit-field.
+struct S {
+  int bit : 4;
+} s;
+
+typeof(s.bit) nope1; // expected-error {{invalid application of 'typeof' to bit-field}}
+typeof_unqual(s.bit) nope2; // expected-error {{invalid application of 'typeof_unqual' to bit-field}}
+
+// Show that we properly resolve nested typeof specifiers.
+extern typeof(typeof(0)) i3;
+extern typeof(typeof(int)) i3;
+extern typeof(typeof_unqual(0)) i3;
+extern typeof(typeof_unqual(int)) i3;
+extern typeof_unqual(typeof(0)) i3;
+extern typeof_unqual(typeof(int)) i3;
+extern typeof_unqual(typeof_unqual(0)) i3;
+extern typeof_unqual(typeof_unqual(int)) i3;
+extern typeof(typeof_unqual(j)) i3;
+extern typeof(typeof_unqual(const int)) i3;
+extern typeof_unqual(typeof(j)) i3;
+extern typeof_unqual(typeof(const int)) i3;
+extern typeof_unqual(typeof_unqual(j)) i3;
+extern typeof_unqual(typeof_unqual(const int)) i3;
+
+// Both of these result in a const int rather than an int.
+extern typeof(typeof(j)) i4;
+extern typeof(typeof(const int)) i4;
+
+// Ensure that redundant qualifiers are allowed, same as with typedefs.
+typedef const int CInt;
+extern CInt i4;
+extern const CInt i4;
+extern const typeof(j) i4;
+extern const typeof(const int) i4;
+extern const typeof(CInt) i4;
+
+// Qualifiers are not redundant here, but validating that the qualifiers are
+// still honored.
+extern const typeof_unqual(j) i4;
+extern const typeof_unqual(const int) i4;
+extern const typeof_unqual(CInt) i4;
+
+// Show that type attributes are stripped from the unqualified version.
+extern __attribute__((address_space(0))) int type_attr_test_2_obj;
+extern int type_attr_test_2;
+extern typeof_unqual(type_attr_test_2_obj) type_attr_test_2;            // expected-note {{previous declaration is here}}
+extern __attribute__((address_space(0))) int type_attr_test_2;          // expected-error {{redeclaration of 'type_attr_test_2' with a different type: '__attribute__((address_space(0))) int' vs 'typeof_unqual (type_attr_test_2_obj)' (aka 'int')}}
+
+// Ensure that an invalid type doesn't cause crashes.
+void invalid_param_fn(__attribute__((address_space(1))) int i); // expected-error {{parameter may not be qualified with an address space}}
+typeof(invalid_param_fn) invalid_param_1;
+typeof_unqual(invalid_param_fn) invalid_param_2;
Index: clang/test/Parser/c2x-typeof.c
===================================================================
--- /dev/null
+++ clang/test/Parser/c2x-typeof.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -verify -std=c2x %s
+
+// Demonstrate that we don't support the expression form without parentheses in
+// C2x mode.
+typeof 0 int i = 12;         // expected-error {{expected '(' after 'typeof'}} expected-error {{expected identifier or '('}}
+typeof 0 j = 12;             // expected-error {{expected '(' after 'typeof'}} expected-error {{expected identifier or '('}}
+typeof_unqual 0 k = 12;      // expected-error {{expected '(' after 'typeof_unqual'}} expected-error {{expected identifier or '('}}
+typeof_unqual 0 int l = 12;  // expected-error {{expected '(' after 'typeof_unqual'}} expected-error {{expected identifier or '('}}
+
+// Show that combining typeof with another type specifier fails, but otherwise
+// the expression and type forms are both parsed properly.
+typeof(0) int a = 12;        // expected-error {{cannot combine with previous 'typeof' declaration specifier}}
+typeof(0) b = 12;
+typeof_unqual(0) int c = 12; // expected-error {{cannot combine with previous 'typeof_unqual' declaration specifier}}
+typeof_unqual(0) d = 12;
+typeof(int) e = 12;
+typeof_unqual(int) f = 12;
+
+// Show that we can parse nested constructs of both forms.
+typeof(typeof(0)) w;
+typeof_unqual(typeof(0)) x;
+typeof(typeof_unqual(0)) y;
+typeof_unqual(typeof_unqual(0)) z;
+
+// Show that you can spell the type in functions, structures, or as the base
+// type of an enumeration.
+typeof(b) func1(typeof(b) c);
+typeof_unqual(b) func2(typeof_unqual(b) c);
+
+struct S {
+  typeof(b) i;
+  typeof_unqual(b) j;
+} s;
+
+enum E1 : typeof(b) { FirstZero };
+enum E2 : typeof_unqual(b) { SecondZero };
+
+// Show that you can use this type in place of another type and everything
+// works as expected.
+_Static_assert(__builtin_offsetof(typeof(struct S), i) == 0);
+_Static_assert(__builtin_offsetof(typeof(s), i) == 0);
+_Static_assert(__builtin_offsetof(typeof_unqual(struct S), i) == 0);
+_Static_assert(__builtin_offsetof(typeof_unqual(s), i) == 0);
+
Index: clang/test/Parser/c2x-typeof-ext-warns.c
===================================================================
--- /dev/null
+++ clang/test/Parser/c2x-typeof-ext-warns.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -verify=c2x -std=c2x %s
+// RUN: %clang_cc1 -verify=c11 -std=c11 %s
+// RUN: %clang_cc1 -verify=gnu11 -std=gnu11 %s
+// RUN: %clang_cc1 -verify=pedantic -pedantic -std=gnu11 -Wno-comment %s
+// RUN: %clang_cc1 -verify=compat -std=c2x -Wpre-c2x-compat %s
+
+// c2x-no-diagnostics
+
+// Exercise the various circumstances under which we will diagnose use of
+// typeof and typeof_unqual as either an extension or as a compatability
+// warning. Note that GCC exposes 'typeof' as a non-conforming extension in
+// standards before C2x, and Clang has followed suit. Neither compiler exposes
+// 'typeof_unqual' as a non-conforming extension.
+
+// Show what happens with the underscored version of the keyword, which is a
+// conforming extension.
+__typeof__(int) i = 12;
+
+// Show what happens with a regular 'typeof' use.
+typeof(i) j = 12; // c11-error {{expected function body after function declarator}} \
+                     pedantic-warning {{extension used}} \
+                     compat-warning {{'typeof' is incompatible with C standards before C2x}}
+
+// Same for 'typeof_unqual'.
+typeof_unqual(j) k = 12; // c11-error {{expected function body after function declarator}} \
+                            gnu11-error {{expected function body after function declarator}} \
+                            pedantic-error {{expected function body after function declarator}} \
+                            compat-warning {{'typeof_unqual' is incompatible with C standards before C2x}}
+
Index: clang/test/Lexer/keywords_test.c
===================================================================
--- clang/test/Lexer/keywords_test.c
+++ clang/test/Lexer/keywords_test.c
@@ -44,6 +44,7 @@
 C2x_KEYWORD(false);
 C2x_KEYWORD(static_assert);
 C2x_KEYWORD(typeof);
+C2x_KEYWORD(typeof_unqual);
 C2x_KEYWORD(thread_local);
 C2x_KEYWORD(alignas);
 C2x_KEYWORD(alignof);
@@ -97,6 +98,7 @@
   char false; // c89-warning {{'false' is a keyword in C2x}}
   float alignof; // c89-warning {{'alignof' is a keyword in C2x}}
   int typeof; // c89-warning {{'typeof' is a keyword in C2x}}
+  int typeof_unqual; // c89-warning {{'typeof_unqual' is a keyword in C2x}}
   int alignas; // c89-warning {{'alignas' is a keyword in C2x}}
   int static_assert; // c89-warning {{'static_assert' is a keyword in C2x}}
 
Index: clang/test/C/C2x/n2930.c
===================================================================
--- /dev/null
+++ clang/test/C/C2x/n2930.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -verify -std=c2x %s
+
+/* WG14 N2930: yes
+ * Consider renaming remove_quals
+ */
+
+int remove_quals;
+int typeof_unqual; // expected-error {{expected '(' after 'typeof_unqual'}}
+typeof_unqual(remove_quals) val;
Index: clang/test/C/C2x/n2927_2.c
===================================================================
--- /dev/null
+++ clang/test/C/C2x/n2927_2.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -emit-llvm -o - -std=c2x %s | FileCheck %s
+
+// C2x 6.7.2.5 EXAMPLE 5
+unsigned long long vla_size(int n) {
+// CHECK: vla_size
+
+  return sizeof(
+    typeof_unqual(char[n + 3])
+  ); // execution-time sizeof, translation-time typeof operation
+// CHECK: [[N_ADDR:%.*]] = alloca i32
+// CHECK: store i32 {{%.*}} ptr [[N_ADDR]]
+// CHECK: [[N:%.*]] = load i32, ptr [[N_ADDR]]
+// CHECK: [[TEMP:%.*]] = add nsw i32 [[N]], 3
+// CHECK: [[RET:%.*]] = zext i32 [[TEMP]] to i64
+// CHECK: ret i64 [[RET]]
+}
+
+int main() {
+  return (int)vla_size(10); // vla_size returns 13
+}
+
Index: clang/test/C/C2x/n2927.c
===================================================================
--- /dev/null
+++ clang/test/C/C2x/n2927.c
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -verify -std=c2x %s
+
+/* WG14 N2927: yes
+ * Not-so-magic: typeof
+ */
+
+// These examples originated in WG14 N2927 but were modified to test particular
+// compiler behaviors. Each of these examples come from C2x 6.7.2.5.
+
+// EXAMPLE 1
+typeof(1 + 1) func();
+int func();
+
+// EXAMPLE 2
+const _Atomic int purr = 0;
+const int meow = 1;
+const char *const mew[] = {
+	"aardvark",
+	"bluejay",
+	"catte",
+};
+
+extern typeof_unqual(purr) plain_purr;
+extern int plain_purr;
+
+extern typeof(_Atomic typeof(meow)) atomic_meow;
+extern const _Atomic int atomic_meow;
+
+extern typeof(mew) mew_array;
+extern const char *const mew_array[3];
+
+extern typeof_unqual(mew) mew2_array;
+extern const char *mew2_array[3];
+
+// EXAMPLE 3
+void foo(int argc, char *argv[]) { // expected-note 2 {{declared here}}
+  _Static_assert(sizeof(typeof('p')) == sizeof(int));
+  _Static_assert(sizeof(typeof('p')) == sizeof('p'));
+  _Static_assert(sizeof(typeof((char)'p')) == sizeof(char));
+  _Static_assert(sizeof(typeof((char)'p')) == sizeof((char)'p'));
+  _Static_assert(sizeof(typeof("meow")) == sizeof(char[5]));
+  _Static_assert(sizeof(typeof("meow")) == sizeof("meow"));
+  _Static_assert(sizeof(typeof(argc)) == sizeof(int));
+  _Static_assert(sizeof(typeof(argc)) == sizeof(argc));
+  _Static_assert(sizeof(typeof(argv)) == sizeof(char**));
+  _Static_assert(sizeof(typeof(argv)) == sizeof(argv)); // expected-warning {{sizeof on array function parameter will return size of 'char **' instead of 'char *[]'}}
+
+  _Static_assert(sizeof(typeof_unqual('p')) == sizeof(int));
+  _Static_assert(sizeof(typeof_unqual('p')) == sizeof('p'));
+  _Static_assert(sizeof(typeof_unqual((char)'p')) == sizeof(char));
+  _Static_assert(sizeof(typeof_unqual((char)'p')) == sizeof((char)'p'));
+  _Static_assert(sizeof(typeof_unqual("meow")) == sizeof(char[5]));
+  _Static_assert(sizeof(typeof_unqual("meow")) == sizeof("meow"));
+  _Static_assert(sizeof(typeof_unqual(argc)) == sizeof(int));
+  _Static_assert(sizeof(typeof_unqual(argc)) == sizeof(argc));
+  _Static_assert(sizeof(typeof_unqual(argv)) == sizeof(char**));
+  _Static_assert(sizeof(typeof_unqual(argv)) == sizeof(argv)); // expected-warning {{sizeof on array function parameter will return size of 'char **' instead of 'char *[]'}}
+}
+
+// EXAMPLE 4
+void bar(int argc) {
+  extern int val;
+  extern typeof(typeof_unqual(typeof(argc)))val;
+}
+
+// EXAMPLE 5 is tested by n2927_2.c because it is a codegen test.
+
+// EXAMPLE 6
+extern const char *y[4];
+extern typeof(typeof(const char*)[4]) y;
+
+// EXAMPLE 7
+void f(int);
+
+void g(double);
+typeof(f(5)) g(double x);          // g has type "void(double)"
+
+extern void (*h)(double);
+extern typeof(g)* h;               // h has type "void(*)(double)"
+extern typeof(true ? g : 0) h;  // h has type "void(*)(double)"
+
+void j(double *, double **);
+void j(double A[5], typeof(A)* B); // j has type "void(double*, double**)"
+
+extern typeof(double[]) D;         // D has an incomplete type
+
+extern double C[2];
+extern typeof(D) C;                // C has type "double[2]"
+
+typeof(D) D = { 5, 8.9, 0.1, 99 }; // D is now completed to "double[4]"
+extern double E[4];
+extern typeof(D) E;                // E has type "double[4]" from D痴 completed type
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -963,12 +963,13 @@
   ///
   /// By default, performs semantic analysis when building the typeof type.
   /// Subclasses may override this routine to provide different behavior.
-  QualType RebuildTypeOfExprType(Expr *Underlying, SourceLocation Loc);
+  QualType RebuildTypeOfExprType(Expr *Underlying, SourceLocation Loc,
+                                 bool IsUnqual);
 
   /// Build a new typeof(type) type.
   ///
   /// By default, builds a new TypeOfType with the given underlying type.
-  QualType RebuildTypeOfType(QualType Underlying);
+  QualType RebuildTypeOfType(QualType Underlying, bool IsUnqual);
 
   /// Build a new unary transform type.
   QualType RebuildUnaryTransformType(QualType BaseType,
@@ -6199,13 +6200,14 @@
     return QualType();
 
   QualType Result = TL.getType();
+  bool IsUnqual = Result->getAs<TypeOfExprType>()->isUnqual();
   if (getDerived().AlwaysRebuild() ||
       E.get() != TL.getUnderlyingExpr()) {
-    Result = getDerived().RebuildTypeOfExprType(E.get(), TL.getTypeofLoc());
+    Result = getDerived().RebuildTypeOfExprType(E.get(), TL.getTypeofLoc(),
+                                                IsUnqual);
     if (Result.isNull())
       return QualType();
   }
-  else E.get();
 
   TypeOfExprTypeLoc NewTL = TLB.push<TypeOfExprTypeLoc>(Result);
   NewTL.setTypeofLoc(TL.getTypeofLoc());
@@ -6224,8 +6226,9 @@
     return QualType();
 
   QualType Result = TL.getType();
+  bool IsUnqual = Result->getAs<TypeOfType>()->isUnqual();
   if (getDerived().AlwaysRebuild() || New_Under_TI != Old_Under_TI) {
-    Result = getDerived().RebuildTypeOfType(New_Under_TI->getType());
+    Result = getDerived().RebuildTypeOfType(New_Under_TI->getType(), IsUnqual);
     if (Result.isNull())
       return QualType();
   }
@@ -14728,14 +14731,15 @@
 }
 
 template <typename Derived>
-QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E,
-                                                       SourceLocation) {
-  return SemaRef.BuildTypeofExprType(E);
+QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E, SourceLocation,
+                                                       bool IsUnqual) {
+  return SemaRef.BuildTypeofExprType(E, IsUnqual);
 }
 
 template<typename Derived>
-QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying) {
-  return SemaRef.Context.getTypeOfType(Underlying);
+QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying,
+                                                   bool IsUnqual) {
+  return SemaRef.Context.getTypeOfType(Underlying, IsUnqual);
 }
 
 template <typename Derived>
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -1610,6 +1610,7 @@
     // TypeQuals handled by caller.
     break;
   }
+  case DeclSpec::TST_typeof_unqualType:
   case DeclSpec::TST_typeofType:
     // FIXME: Preserve type source info.
     Result = S.GetTypeFromParser(DS.getRepAsType());
@@ -1618,13 +1619,16 @@
       if (const TagType *TT = Result->getAs<TagType>())
         S.DiagnoseUseOfDecl(TT->getDecl(), DS.getTypeSpecTypeLoc());
     // TypeQuals handled by caller.
-    Result = Context.getTypeOfType(Result);
+    Result = Context.getTypeOfType(Result, DS.getTypeSpecType() ==
+                                               DeclSpec::TST_typeof_unqualType);
     break;
+  case DeclSpec::TST_typeof_unqualExpr:
   case DeclSpec::TST_typeofExpr: {
     Expr *E = DS.getRepAsExpr();
     assert(E && "Didn't get an expression for typeof?");
     // TypeQuals handled by caller.
-    Result = S.BuildTypeofExprType(E);
+    Result = S.BuildTypeofExprType(E, DS.getTypeSpecType() ==
+                                          DeclSpec::TST_typeof_unqualExpr);
     if (Result.isNull()) {
       Result = Context.IntTy;
       declarator.setInvalidType(true);
@@ -6063,12 +6067,14 @@
 
     }
     void VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) {
-      assert(DS.getTypeSpecType() == DeclSpec::TST_typeofExpr);
+      assert(DS.getTypeSpecType() == DeclSpec::TST_typeofExpr ||
+             DS.getTypeSpecType() == DeclSpec::TST_typeof_unqualExpr);
       TL.setTypeofLoc(DS.getTypeSpecTypeLoc());
       TL.setParensRange(DS.getTypeofParensRange());
     }
     void VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
-      assert(DS.getTypeSpecType() == DeclSpec::TST_typeofType);
+      assert(DS.getTypeSpecType() == DeclSpec::TST_typeofType ||
+             DS.getTypeSpecType() == DeclSpec::TST_typeof_unqualType);
       TL.setTypeofLoc(DS.getTypeSpecTypeLoc());
       TL.setParensRange(DS.getTypeofParensRange());
       assert(DS.getRepAsType());
@@ -9123,18 +9129,19 @@
       Keyword, SS.isValid() ? SS.getScopeRep() : nullptr, T, OwnedTagDecl);
 }
 
-QualType Sema::BuildTypeofExprType(Expr *E) {
+QualType Sema::BuildTypeofExprType(Expr *E, bool IsUnqual) {
   assert(!E->hasPlaceholderType() && "unexpected placeholder");
 
   if (!getLangOpts().CPlusPlus && E->refersToBitField())
-    Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 2;
+    Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield)
+        << (IsUnqual ? 3 : 2);
 
   if (!E->isTypeDependent()) {
     QualType T = E->getType();
     if (const TagType *TT = T->getAs<TagType>())
       DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc());
   }
-  return Context.getTypeOfExprType(E);
+  return Context.getTypeOfExprType(E, IsUnqual);
 }
 
 /// getDecltypeForExpr - Given an expr, will return the decltype for
Index: clang/lib/Sema/SemaTemplateVariadic.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateVariadic.cpp
+++ clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -863,6 +863,7 @@
   const DeclSpec &DS = D.getDeclSpec();
   switch (DS.getTypeSpecType()) {
   case TST_typename:
+  case TST_typeof_unqualType:
   case TST_typeofType:
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
@@ -873,6 +874,7 @@
     break;
   }
 
+  case TST_typeof_unqualExpr:
   case TST_typeofExpr:
   case TST_decltype:
   case TST_bitint:
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -5923,6 +5923,7 @@
   switch (DS.getTypeSpecType()) {
   case DeclSpec::TST_typename:
   case DeclSpec::TST_typeofType:
+  case DeclSpec::TST_typeof_unqualType:
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case DeclSpec::TST_##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
   case DeclSpec::TST_atomic: {
@@ -5948,6 +5949,7 @@
   }
 
   case DeclSpec::TST_decltype:
+  case DeclSpec::TST_typeof_unqualExpr:
   case DeclSpec::TST_typeofExpr: {
     Expr *E = DS.getRepAsExpr();
     ExprResult Result = S.RebuildExprInCurrentInstantiation(E);
Index: clang/lib/Sema/DeclSpec.cpp
===================================================================
--- clang/lib/Sema/DeclSpec.cpp
+++ clang/lib/Sema/DeclSpec.cpp
@@ -384,6 +384,7 @@
       return false;
 
     case TST_decltype:
+    case TST_typeof_unqualExpr:
     case TST_typeofExpr:
       if (Expr *E = DS.getRepAsExpr())
         return E->getType()->isFunctionType();
@@ -392,6 +393,7 @@
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
     case TST_typename:
+    case TST_typeof_unqualType:
     case TST_typeofType: {
       QualType QT = DS.getRepAsType().get();
       if (QT.isNull())
@@ -573,6 +575,8 @@
   case DeclSpec::TST_typename:    return "type-name";
   case DeclSpec::TST_typeofType:
   case DeclSpec::TST_typeofExpr:  return "typeof";
+  case DeclSpec::TST_typeof_unqualType:
+  case DeclSpec::TST_typeof_unqualExpr: return "typeof_unqual";
   case DeclSpec::TST_auto:        return "auto";
   case DeclSpec::TST_auto_type:   return "__auto_type";
   case DeclSpec::TST_decltype:    return "(decltype)";
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -2287,6 +2287,13 @@
 ///           typeof ( expressions )
 ///           typeof ( type-name )
 /// [GNU/C++] typeof unary-expression
+/// [C2x]   typeof-specifier:
+///           typeof '(' typeof-specifier-argument ')'
+///           typeof_unqual '(' typeof-specifier-argument ')'
+///
+///         typeof-specifier-argument:
+///           expression
+///           type-name
 ///
 /// [OpenCL 1.1 6.11.12] vec_step built-in function:
 ///           vec_step ( expressions )
@@ -2298,8 +2305,9 @@
                                            ParsedType &CastTy,
                                            SourceRange &CastRange) {
 
-  assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_sizeof, tok::kw___alignof,
-                       tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
+  assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof,
+                       tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof,
+                       tok::kw_vec_step,
                        tok::kw___builtin_omp_required_simd_align) &&
          "Not a typeof/sizeof/alignof/vec_step expression!");
 
@@ -2335,7 +2343,8 @@
     }
 
     isCastExpr = false;
-    if (OpTok.is(tok::kw_typeof) && !getLangOpts().CPlusPlus) {
+    if (OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
+        !getLangOpts().CPlusPlus) {
       Diag(Tok, diag::err_expected_after) << OpTok.getIdentifierInfo()
                                           << tok::l_paren;
       return ExprError();
@@ -2361,7 +2370,8 @@
       return ExprEmpty();
     }
 
-    if (getLangOpts().CPlusPlus || OpTok.isNot(tok::kw_typeof)) {
+    if (getLangOpts().CPlusPlus ||
+        !OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual)) {
       // GNU typeof in C requires the expression to be parenthesized. Not so for
       // sizeof/alignof or in C++. Therefore, the parenthesized expression is
       // the start of a unary-expression, but doesn't include any postfix
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -4183,8 +4183,9 @@
         continue;
       break;
 
-    // GNU typeof support.
+    // C2x/GNU typeof support.
     case tok::kw_typeof:
+    case tok::kw_typeof_unqual:
       ParseTypeofSpecifier(DS);
       continue;
 
@@ -5190,8 +5191,9 @@
 
     // GNU attributes support.
   case tok::kw___attribute:
-    // GNU typeof support.
+    // C2x/GNU typeof support.
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
 
     // type-specifiers
   case tok::kw_short:
@@ -5429,8 +5431,9 @@
   case tok::kw_static_assert:
   case tok::kw__Static_assert:
 
-    // GNU typeof support.
+    // C2x/GNU typeof support.
   case tok::kw_typeof:
+  case tok::kw_typeof_unqual:
 
     // GNU attributes.
   case tok::kw___attribute:
@@ -7437,13 +7440,27 @@
 ///           typeof ( expressions )
 ///           typeof ( type-name )
 /// [GNU/C++] typeof unary-expression
+/// [C2x]   typeof-specifier:
+///           typeof '(' typeof-specifier-argument ')'
+///           typeof_unqual '(' typeof-specifier-argument ')'
+///
+///         typeof-specifier-argument:
+///           expression
+///           type-name
 ///
 void Parser::ParseTypeofSpecifier(DeclSpec &DS) {
-  assert(Tok.is(tok::kw_typeof) && "Not a typeof specifier");
+  assert(Tok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual) &&
+         "Not a typeof specifier");
+
+  bool IsUnqual = Tok.is(tok::kw_typeof_unqual);
+  const IdentifierInfo *II = Tok.getIdentifierInfo();
+  if (getLangOpts().C2x && !II->getName().startswith("__"))
+    Diag(Tok.getLocation(), diag::warn_c2x_compat_typeof_type_specifier)
+        << IsUnqual;
+
   Token OpTok = Tok;
   SourceLocation StartLoc = ConsumeToken();
-
-  const bool hasParens = Tok.is(tok::l_paren);
+  bool HasParens = Tok.is(tok::l_paren);
 
   EnterExpressionEvaluationContext Unevaluated(
       Actions, Sema::ExpressionEvaluationContext::Unevaluated,
@@ -7454,7 +7471,7 @@
   SourceRange CastRange;
   ExprResult Operand = Actions.CorrectDelayedTyposInExpr(
       ParseExprAfterUnaryExprOrTypeTrait(OpTok, isCastExpr, CastTy, CastRange));
-  if (hasParens)
+  if (HasParens)
     DS.setTypeArgumentRange(CastRange);
 
   if (CastRange.getEnd().isInvalid())
@@ -7472,7 +7489,9 @@
     const char *PrevSpec = nullptr;
     unsigned DiagID;
     // Check for duplicate type specifiers (e.g. "int typeof(int)").
-    if (DS.SetTypeSpecType(DeclSpec::TST_typeofType, StartLoc, PrevSpec,
+    if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualType
+                                    : DeclSpec::TST_typeofType,
+                           StartLoc, PrevSpec,
                            DiagID, CastTy,
                            Actions.getASTContext().getPrintingPolicy()))
       Diag(StartLoc, DiagID) << PrevSpec;
@@ -7495,7 +7514,9 @@
   const char *PrevSpec = nullptr;
   unsigned DiagID;
   // Check for duplicate type specifiers (e.g. "int typeof(int)").
-  if (DS.SetTypeSpecType(DeclSpec::TST_typeofExpr, StartLoc, PrevSpec,
+  if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualExpr
+                                  : DeclSpec::TST_typeofExpr,
+                         StartLoc, PrevSpec,
                          DiagID, Operand.get(),
                          Actions.getASTContext().getPrintingPolicy()))
     Diag(StartLoc, DiagID) << PrevSpec;
Index: clang/lib/Lex/Preprocessor.cpp
===================================================================
--- clang/lib/Lex/Preprocessor.cpp
+++ clang/lib/Lex/Preprocessor.cpp
@@ -58,6 +58,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Capacity.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MemoryBuffer.h"
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1110,7 +1110,7 @@
 
 void TypePrinter::printTypeOfExprBefore(const TypeOfExprType *T,
                                         raw_ostream &OS) {
-  OS << "typeof ";
+  OS << (T->isUnqual() ? "typeof_unqual " : "typeof ");
   if (T->getUnderlyingExpr())
     T->getUnderlyingExpr()->printPretty(OS, nullptr, Policy);
   spaceBeforePlaceHolder(OS);
@@ -1120,7 +1120,7 @@
                                        raw_ostream &OS) {}
 
 void TypePrinter::printTypeOfBefore(const TypeOfType *T, raw_ostream &OS) {
-  OS << "typeof(";
+  OS << (T->isUnqual() ? "typeof_unqual(" : "typeof(");
   print(T->getUnderlyingType(), OS, StringRef());
   OS << ')';
   spaceBeforePlaceHolder(OS);
Index: clang/lib/AST/Type.cpp
===================================================================
--- clang/lib/AST/Type.cpp
+++ clang/lib/AST/Type.cpp
@@ -3469,27 +3469,49 @@
   return Inner;
 }
 
-TypeOfExprType::TypeOfExprType(Expr *E, QualType can)
-    : Type(TypeOfExpr, can,
+TypeOfExprType::TypeOfExprType(Expr *E, bool IsUnqual, QualType Can)
+    : Type(TypeOfExpr, IsUnqual ? Can.getTypeofUnqualType() : Can,
            toTypeDependence(E->getDependence()) |
                (E->getType()->getDependence() &
                 TypeDependence::VariablyModified)),
-      TOExpr(E) {}
+      TOExpr(E), IsUnqual(IsUnqual) {}
 
 bool TypeOfExprType::isSugared() const {
   return !TOExpr->isTypeDependent();
 }
 
 QualType TypeOfExprType::desugar() const {
-  if (isSugared())
-    return getUnderlyingExpr()->getType();
-
+  if (isSugared()) {
+    QualType QT = getUnderlyingExpr()->getType();
+    return IsUnqual ? QT.getTypeofUnqualType() : QT;
+  }
   return QualType(this, 0);
 }
 
+QualType QualType::getTypeofUnqualType() const {
+  if (isNull())
+    return *this;
+
+  // C2x 6.7.2.5p5:
+  // The result of the typeof_unqual operation is the non-atomic unqualified
+  // version of the type name that would result from the typeof operation.
+  // The typeof operator preserves all qualifiers.
+  QualType Ret = getAtomicUnqualifiedType();
+
+  // We strip all qualifier-like attributes as well.
+  if (const auto *AT =
+          dyn_cast_if_present<AttributedType>(Ret.getTypePtrOrNull());
+      AT && AT->isQualifier())
+    Ret = AT->getModifiedType();
+
+  return Ret;
+}
+
 void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
-                                      const ASTContext &Context, Expr *E) {
+                                      const ASTContext &Context, Expr *E,
+                                      bool IsUnqual) {
   E->Profile(ID, Context, true);
+  ID.AddBoolean(IsUnqual);
 }
 
 DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -1370,16 +1370,15 @@
   ExpectedExpr ToExprOrErr = import(T->getUnderlyingExpr());
   if (!ToExprOrErr)
     return ToExprOrErr.takeError();
-
-  return Importer.getToContext().getTypeOfExprType(*ToExprOrErr);
+  return Importer.getToContext().getTypeOfExprType(*ToExprOrErr, T->isUnqual());
 }
 
 ExpectedType ASTNodeImporter::VisitTypeOfType(const TypeOfType *T) {
   ExpectedType ToUnderlyingTypeOrErr = import(T->getUnderlyingType());
   if (!ToUnderlyingTypeOrErr)
     return ToUnderlyingTypeOrErr.takeError();
-
-  return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr);
+  return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr,
+                                               T->isUnqual());
 }
 
 ExpectedType ASTNodeImporter::VisitUsingType(const UsingType *T) {
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5579,11 +5579,11 @@
 /// multiple declarations that refer to "typeof(x)" all contain different
 /// DeclRefExpr's. This doesn't effect the type checker, since it operates
 /// on canonical type's (which are always unique).
-QualType ASTContext::getTypeOfExprType(Expr *tofExpr) const {
+QualType ASTContext::getTypeOfExprType(Expr *tofExpr, bool IsUnqual) const {
   TypeOfExprType *toe;
   if (tofExpr->isTypeDependent()) {
     llvm::FoldingSetNodeID ID;
-    DependentTypeOfExprType::Profile(ID, *this, tofExpr);
+    DependentTypeOfExprType::Profile(ID, *this, tofExpr, IsUnqual);
 
     void *InsertPos = nullptr;
     DependentTypeOfExprType *Canon
@@ -5591,18 +5591,19 @@
     if (Canon) {
       // We already have a "canonical" version of an identical, dependent
       // typeof(expr) type. Use that as our canonical type.
-      toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr,
+      toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, IsUnqual,
                                           QualType((TypeOfExprType*)Canon, 0));
     } else {
       // Build a new, canonical typeof(expr) type.
-      Canon
-        = new (*this, TypeAlignment) DependentTypeOfExprType(*this, tofExpr);
+      Canon = new (*this, TypeAlignment)
+          DependentTypeOfExprType(*this, tofExpr, IsUnqual);
       DependentTypeOfExprTypes.InsertNode(Canon, InsertPos);
       toe = Canon;
     }
   } else {
     QualType Canonical = getCanonicalType(tofExpr->getType());
-    toe = new (*this, TypeAlignment) TypeOfExprType(tofExpr, Canonical);
+    toe =
+        new (*this, TypeAlignment) TypeOfExprType(tofExpr, IsUnqual, Canonical);
   }
   Types.push_back(toe);
   return QualType(toe, 0);
@@ -5613,9 +5614,10 @@
 /// memory savings. Since typeof(t) is fairly uncommon, space shouldn't be
 /// an issue. This doesn't affect the type checker, since it operates
 /// on canonical types (which are always unique).
-QualType ASTContext::getTypeOfType(QualType tofType) const {
+QualType ASTContext::getTypeOfType(QualType tofType, bool IsUnqual) const {
   QualType Canonical = getCanonicalType(tofType);
-  auto *tot = new (*this, TypeAlignment) TypeOfType(tofType, Canonical);
+  auto *tot =
+      new (*this, TypeAlignment) TypeOfType(tofType, Canonical, IsUnqual);
   Types.push_back(tot);
   return QualType(tot, 0);
 }
@@ -12905,7 +12907,13 @@
     return Ctx.getTypedefType(CD, Ctx.getQualifiedType(Underlying));
   }
   case Type::TypeOf:
-    return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying));
+    // FIXME:: is this assumption correct or do we need to do work here to find
+    // the common type sugar regarding the stripped qualifiers if only one side
+    // is unqual?
+    assert(cast<TypeOfType>(X)->isUnqual() == cast<TypeOfType>(Y)->isUnqual() &&
+           "typeof vs typeof_unqual mismatch?");
+    return Ctx.getTypeOfType(Ctx.getQualifiedType(Underlying),
+                             cast<TypeOfType>(X)->isUnqual());
   case Type::TypeOfExpr:
     return QualType();
 
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2516,7 +2516,7 @@
   // Returns the underlying type of a decltype with the given expression.
   QualType getDecltypeForExpr(Expr *E);
 
-  QualType BuildTypeofExprType(Expr *E);
+  QualType BuildTypeofExprType(Expr *E, bool IsUnqual);
   /// If AsUnevaluated is false, E is treated as though it were an evaluated
   /// context, such as when building a type for decltype(auto).
   QualType BuildDecltypeType(Expr *E, bool AsUnevaluated = true);
Index: clang/include/clang/Sema/DeclSpec.h
===================================================================
--- clang/include/clang/Sema/DeclSpec.h
+++ clang/include/clang/Sema/DeclSpec.h
@@ -289,6 +289,8 @@
   static const TST TST_typename = clang::TST_typename;
   static const TST TST_typeofType = clang::TST_typeofType;
   static const TST TST_typeofExpr = clang::TST_typeofExpr;
+  static const TST TST_typeof_unqualType = clang::TST_typeof_unqualType;
+  static const TST TST_typeof_unqualExpr = clang::TST_typeof_unqualExpr;
   static const TST TST_decltype = clang::TST_decltype;
   static const TST TST_decltype_auto = clang::TST_decltype_auto;
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait)                                     \
@@ -404,10 +406,11 @@
 
   static bool isTypeRep(TST T) {
     return T == TST_atomic || T == TST_typename || T == TST_typeofType ||
-           isTransformTypeTrait(T);
+           T == TST_typeof_unqualType || isTransformTypeTrait(T);
   }
   static bool isExprRep(TST T) {
-    return (T == TST_typeofExpr || T == TST_decltype || T == TST_bitint);
+    return T == TST_typeofExpr || T == TST_typeof_unqualExpr ||
+           T == TST_decltype || T == TST_bitint;
   }
   static bool isTemplateIdRep(TST T) {
     return (T == TST_auto || T == TST_decltype_auto);
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -31,6 +31,9 @@
 #ifndef C99_KEYWORD
 #define C99_KEYWORD(X,Y) KEYWORD(X,KEYC99|(Y))
 #endif
+#ifndef C2X_KEYWORD
+#define C2X_KEYWORD(X,Y) KEYWORD(X,KEYC2X|(Y))
+#endif
 #ifndef COROUTINES_KEYWORD
 #define COROUTINES_KEYWORD(X) CXX20_KEYWORD(X,KEYCOROUTINES)
 #endif
@@ -412,6 +415,10 @@
 // C11 Extension
 KEYWORD(_Float16                    , KEYALL)
 
+// C2x keywords
+C2X_KEYWORD(typeof                  , KEYGNU)
+C2X_KEYWORD(typeof_unqual           , 0)
+
 // ISO/IEC JTC1 SC22 WG14 N1169 Extension
 KEYWORD(_Accum                      , KEYNOCXX)
 KEYWORD(_Fract                      , KEYNOCXX)
@@ -450,9 +457,6 @@
 KEYWORD(__PRETTY_FUNCTION__         , KEYALL)
 KEYWORD(__auto_type                 , KEYALL)
 
-// GNU Extensions (outside impl-reserved namespace)
-KEYWORD(typeof                      , KEYGNU|KEYC2X)
-
 // MS Extensions
 KEYWORD(__FUNCDNAME__               , KEYMS)
 KEYWORD(__FUNCSIG__                 , KEYMS)
@@ -943,3 +947,4 @@
 #undef PUNCTUATOR
 #undef TOK
 #undef C99_KEYWORD
+#undef C2X_KEYWORD
Index: clang/include/clang/Basic/Specifiers.h
===================================================================
--- clang/include/clang/Basic/Specifiers.h
+++ clang/include/clang/Basic/Specifiers.h
@@ -79,8 +79,10 @@
     TST_class,     // C++ class type
     TST_interface, // C++ (Microsoft-specific) __interface type
     TST_typename,  // Typedef, C++ class-name or enum name, etc.
-    TST_typeofType,
-    TST_typeofExpr,
+    TST_typeofType,        // C2x (and GNU extension) typeof(type-name)
+    TST_typeofExpr,        // C2x (and GNU extension) typeof(expression)
+    TST_typeof_unqualType, // C2x typeof_unqual(type-name)
+    TST_typeof_unqualExpr, // C2x typeof_unqual(expression)
     TST_decltype, // C++11 decltype
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait,
 #include "clang/Basic/TransformTypeTraits.def"
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6517,7 +6517,8 @@
 def err_openmp_default_simd_align_expr : Error<
   "invalid application of '__builtin_omp_required_simd_align' to an expression, only type is allowed">;
 def err_sizeof_alignof_typeof_bitfield : Error<
-  "invalid application of '%select{sizeof|alignof|typeof}0' to bit-field">;
+  "invalid application of '%select{sizeof|alignof|typeof|typeof_unqual}0' to "
+  "bit-field">;
 def err_alignof_member_of_incomplete_type : Error<
   "invalid application of 'alignof' to a field of a class still being defined">;
 def err_vecstep_non_scalar_vector_type : Error<
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -373,6 +373,9 @@
 def ext_auto_type : Extension<
   "'__auto_type' is a GNU extension">,
   InGroup<GNUAutoType>;
+def warn_c2x_compat_typeof_type_specifier : Warning<
+  "'%select{typeof|typeof_unqual}0' is incompatible with C standards before "
+  "C2x">, InGroup<CPre2xCompat>, DefaultIgnore;
 def ext_for_range : ExtWarn<
   "range-based for loop is a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_for_range : Warning<
Index: clang/include/clang/AST/TypeProperties.td
===================================================================
--- clang/include/clang/AST/TypeProperties.td
+++ clang/include/clang/AST/TypeProperties.td
@@ -397,8 +397,12 @@
     let Read = [{ node->getUnderlyingExpr() }];
   }
 
+  def : Property<"isUnqual", Bool> {
+    let Read = [{ node->isUnqual() }];
+  }
+
   def : Creator<[{
-    return ctx.getTypeOfExprType(expression);
+    return ctx.getTypeOfExprType(expression, isUnqual);
   }]>;
 }
 
@@ -407,8 +411,12 @@
     let Read = [{ node->getUnderlyingType() }];
   }
 
+  def : Property<"isUnqual", Bool> {
+    let Read = [{ node->isUnqual() }];
+  }
+
   def : Creator<[{
-    return ctx.getTypeOfType(underlyingType);
+    return ctx.getTypeOfType(underlyingType, isUnqual);
   }]>;
 }
 
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -1068,6 +1068,9 @@
     return *this;
   }
 
+  /// Returns the typeof_unqual-unqualified version of the type.
+  QualType getTypeofUnqualType() const;
+
   /// Indicate whether the specified types and qualifiers are identical.
   friend bool operator==(const QualType &LHS, const QualType &RHS) {
     return LHS.Value == RHS.Value;
@@ -4527,18 +4530,23 @@
   }
 };
 
-/// Represents a `typeof` (or __typeof__) expression (a GCC extension).
+/// Represents a `typeof` (or __typeof__) expression (a C2x feature and GCC
+/// extension) or a `typeof_unqual` expression (a C2x feature).
 class TypeOfExprType : public Type {
   Expr *TOExpr;
+  bool IsUnqual;
 
 protected:
   friend class ASTContext; // ASTContext creates these.
 
-  TypeOfExprType(Expr *E, QualType can = QualType());
+  TypeOfExprType(Expr *E, bool IsUnqual, QualType Can = QualType());
 
 public:
   Expr *getUnderlyingExpr() const { return TOExpr; }
 
+  /// Returns true if this is a typeof_unqual type.
+  bool isUnqual() const { return IsUnqual; }
+
   /// Remove a single level of sugar.
   QualType desugar() const;
 
@@ -4559,37 +4567,46 @@
   const ASTContext &Context;
 
 public:
-  DependentTypeOfExprType(const ASTContext &Context, Expr *E)
-      : TypeOfExprType(E), Context(Context) {}
+  DependentTypeOfExprType(const ASTContext &Context, Expr *E, bool IsUnqual)
+      : TypeOfExprType(E, IsUnqual), Context(Context) {}
 
   void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, Context, getUnderlyingExpr());
+    Profile(ID, Context, getUnderlyingExpr(), isUnqual());
   }
 
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-                      Expr *E);
+                      Expr *E, bool IsUnqual);
 };
 
-/// Represents `typeof(type)`, a GCC extension.
+/// Represents `typeof(type)`, a C2x feature and GCC extension, or
+/// `typeof_unqual(type), a C2x feature.
 class TypeOfType : public Type {
   friend class ASTContext; // ASTContext creates these.
 
   QualType TOType;
+  bool IsUnqual; // typeof_unqual(type-name)
 
-  TypeOfType(QualType T, QualType can)
-      : Type(TypeOf, can, T->getDependence()), TOType(T) {
-    assert(!isa<TypedefType>(can) && "Invalid canonical type");
+  TypeOfType(QualType T, QualType Can, bool IsUnqual)
+      : Type(TypeOf, IsUnqual ? Can.getTypeofUnqualType() : Can,
+          T->getDependence()), TOType(T), IsUnqual(IsUnqual) {
+    assert(!isa<TypedefType>(Can) && "Invalid canonical type");
   }
 
 public:
   QualType getUnderlyingType() const { return TOType; }
 
   /// Remove a single level of sugar.
-  QualType desugar() const { return getUnderlyingType(); }
+  QualType desugar() const {
+    QualType QT = getUnderlyingType();
+    return IsUnqual ? QT.getTypeofUnqualType() : QT;
+  }
 
   /// Returns whether this type directly provides sugar.
   bool isSugared() const { return true; }
 
+  /// Returns true if this is a typeof_unqual type.
+  bool isUnqual() const { return IsUnqual; }
+
   static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
 };
 
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -1715,8 +1715,8 @@
   QualType getObjCObjectPointerType(QualType OIT) const;
 
   /// GCC extension.
-  QualType getTypeOfExprType(Expr *e) const;
-  QualType getTypeOfType(QualType t) const;
+  QualType getTypeOfExprType(Expr *E, bool IsUnqual) const;
+  QualType getTypeOfType(QualType QT, bool IsUnqual) const;
 
   QualType getReferenceQualifiedType(const Expr *e) const;
 
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -275,6 +275,22 @@
   ``-Wunused-label`` warning.
 - Implemented `WG14 N2508 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2508.pdf>`_,
   so labels can placed everywhere inside a compound statement.
+- Implemented `WG14 N2927 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm>`_,
+  the Not-so-magic ``typeof`` operator. Also implemented
+  `WG14 N2930 <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2930.pdf>`_,
+  renaming ``remove_quals``, so the ``typeof_unqual`` operator is also
+  supported. Both of these operators are supported only in C2x mode. The
+  ``typeof`` operator specifies the type of the given parenthesized expression
+  operand or type name, including all qualifiers. The ``typeof_unqual``
+  operator is similar to ``typeof`` except that all qualifiers are removed,
+  including atomic type qualification and type attributes which behave like a
+  qualifier, such as an address space attribute.
+
+  .. code-block:: c
+
+    __attribute__((address_space(1))) const _Atomic int Val;
+    typeof(Val) OtherVal; // type is '__attribute__((address_space(1))) const _Atomic int'
+    typeof_unqual(Val) OtherValUnqual; // type is 'int'
 
 C++ Language Changes in Clang
 -----------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to