aaron.ballman created this revision.
aaron.ballman added reviewers: erichkeane, jyknight, rsmith, rjmccall, 
clang-language-wg.
Herald added a subscriber: martong.
Herald added a reviewer: shafik.
Herald added a project: All.
aaron.ballman requested review of this revision.
Herald added a project: clang.

This implements WG14 N2927 and WG14 N2930, which together define the feature 
for `typeof` and `typeof_unqual`, which get the type of their argument as 
either fully qualified or fully unqualified. The argument to either operator is 
either a type name or an expression. If given a type name, the type information 
is pulled directly from the given name. If given an expression, the type 
information is pulled from the expression. Recursive use of these operators is 
allowed and has the expected behavior (the innermost operator is resolved to a 
type, and that's used to resolve the next layer of typeof specifier, until a 
fully resolved type is determined.

Note, we already supported `typeof` in GNU mode as a non-conforming extension 
and we are *not* exposing `typeof_unqual` as a non-conforming extension in that 
mode, nor are we exposing `typeof` or `typeof_unqual` as a nonconforming 
extension in other language modes. The GNU variant of `typeof` supports a form 
where the parentheses are elided from the operator when given an expression 
(e.g., `typeof 0 i = 12;`). When in C2x mode, we do not support this extension.


Repository:
  rG LLVM Github Monorepo

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

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