aaron.ballman updated this revision to Diff 461675.
aaron.ballman added a comment.
Really update, don't just kinda sorta update.
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/PropertiesBase.td
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/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,
+ TypeOfKind Kind);
/// 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, TypeOfKind Kind);
/// Build a new unary transform type.
QualType RebuildUnaryTransformType(QualType BaseType,
@@ -6199,13 +6200,15 @@
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 ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
if (Result.isNull())
return QualType();
}
- else E.get();
TypeOfExprTypeLoc NewTL = TLB.push<TypeOfExprTypeLoc>(Result);
NewTL.setTypeofLoc(TL.getTypeofLoc());
@@ -6224,8 +6227,11 @@
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 ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified);
if (Result.isNull())
return QualType();
}
@@ -14728,14 +14734,15 @@
}
template <typename Derived>
-QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E,
- SourceLocation) {
- return SemaRef.BuildTypeofExprType(E);
+QualType TreeTransform<Derived>::RebuildTypeOfExprType(Expr *E, SourceLocation,
+ TypeOfKind Kind) {
+ return SemaRef.BuildTypeofExprType(E, Kind);
}
template<typename Derived>
-QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying) {
- return SemaRef.Context.getTypeOfType(Underlying);
+QualType TreeTransform<Derived>::RebuildTypeOfType(QualType Underlying,
+ TypeOfKind Kind) {
+ return SemaRef.Context.getTypeOfType(Underlying, Kind);
}
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,20 @@
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
+ ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified);
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
+ ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified);
if (Result.isNull()) {
Result = Context.IntTy;
declarator.setInvalidType(true);
@@ -6083,12 +6091,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());
@@ -9143,18 +9153,19 @@
Keyword, SS.isValid() ? SS.getScopeRep() : nullptr, T, OwnedTagDecl);
}
-QualType Sema::BuildTypeofExprType(Expr *E) {
+QualType Sema::BuildTypeofExprType(Expr *E, TypeOfKind Kind) {
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)
+ << (Kind == TypeOfKind::Unqualified ? 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, Kind);
}
/// 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/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,51 @@
return Inner;
}
-TypeOfExprType::TypeOfExprType(Expr *E, QualType can)
- : Type(TypeOfExpr, can,
+TypeOfExprType::TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can)
+ : Type(TypeOfExpr,
+ Kind == TypeOfKind::Unqualified ? Can.getTypeofUnqualType() : Can,
toTypeDependence(E->getDependence()) |
(E->getType()->getDependence() &
TypeDependence::VariablyModified)),
- TOExpr(E) {}
+ TOExpr(E) {
+ TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
+}
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<AttributedType>(Ret.getTypePtr());
+ 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,17 @@
ExpectedExpr ToExprOrErr = import(T->getUnderlyingExpr());
if (!ToExprOrErr)
return ToExprOrErr.takeError();
-
- return Importer.getToContext().getTypeOfExprType(*ToExprOrErr);
+ return Importer.getToContext().getTypeOfExprType(
+ *ToExprOrErr,
+ T->isUnqual() ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
}
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() ? TypeOfKind::Unqualified : TypeOfKind::Qualified);
}
ExpectedType ASTNodeImporter::VisitUsingType(const UsingType *T) {
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -5587,30 +5587,31 @@
/// 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, TypeOfKind Kind) const {
TypeOfExprType *toe;
if (tofExpr->isTypeDependent()) {
llvm::FoldingSetNodeID ID;
- DependentTypeOfExprType::Profile(ID, *this, tofExpr);
+ DependentTypeOfExprType::Profile(ID, *this, tofExpr,
+ Kind == TypeOfKind::Unqualified);
void *InsertPos = nullptr;
- DependentTypeOfExprType *Canon
- = DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos);
+ DependentTypeOfExprType *Canon =
+ DependentTypeOfExprTypes.FindNodeOrInsertPos(ID, InsertPos);
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,
- QualType((TypeOfExprType*)Canon, 0));
+ toe = new (*this, TypeAlignment)
+ TypeOfExprType(tofExpr, Kind, 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, Kind);
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, Kind, Canonical);
}
Types.push_back(toe);
return QualType(toe, 0);
@@ -5621,9 +5622,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, TypeOfKind Kind) const {
QualType Canonical = getCanonicalType(tofType);
- auto *tot = new (*this, TypeAlignment) TypeOfType(tofType, Canonical);
+ auto *tot =
+ new (*this, TypeAlignment) TypeOfType(tofType, Canonical, Kind);
Types.push_back(tot);
return QualType(tot, 0);
}
@@ -12913,7 +12915,15 @@
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()
+ ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified);
case Type::TypeOfExpr:
return QualType();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2519,7 +2519,7 @@
// Returns the underlying type of a decltype with the given expression.
QualType getDecltypeForExpr(Expr *E);
- QualType BuildTypeofExprType(Expr *E);
+ QualType BuildTypeofExprType(Expr *E, TypeOfKind Kind);
/// 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
@@ -6520,7 +6520,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,13 @@
let Read = [{ node->getUnderlyingExpr() }];
}
+ def : Property<"kind", TypeOfKind> {
+ let Read = [{ node->isUnqual() ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified }];
+ }
+
def : Creator<[{
- return ctx.getTypeOfExprType(expression);
+ return ctx.getTypeOfExprType(expression, kind);
}]>;
}
@@ -407,8 +412,13 @@
let Read = [{ node->getUnderlyingType() }];
}
+ def : Property<"kind", TypeOfKind> {
+ let Read = [{ node->isUnqual() ? TypeOfKind::Unqualified
+ : TypeOfKind::Qualified }];
+ }
+
def : Creator<[{
- return ctx.getTypeOfType(underlyingType);
+ return ctx.getTypeOfType(underlyingType, kind);
}]>;
}
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -714,6 +714,12 @@
Superclass,
};
+/// The kind of 'typeof' expression we're after.
+enum class TypeOfKind : uint8_t {
+ Qualified,
+ Unqualified,
+};
+
/// A (possibly-)qualified type.
///
/// For efficiency, we don't store CV-qualified types as nodes on their
@@ -1068,6 +1074,11 @@
return *this;
}
+ /// Returns the type with all qualifications stripped, including atomic
+ /// qualification and qualifier-like attributes. Used to determine the type
+ /// for an 'typeof_unqual' type specifier.
+ 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;
@@ -1793,6 +1804,14 @@
unsigned NumArgs;
};
+ class TypeOfBitfields {
+ friend class TypeOfType;
+ friend class TypeOfExprType;
+
+ unsigned : NumTypeBits;
+ unsigned IsUnqual : 1; // If true: typeof_unqual, else: typeof
+ };
+
class SubstTemplateTypeParmTypeBitfields {
friend class SubstTemplateTypeParmType;
@@ -1880,6 +1899,7 @@
ConstantArrayTypeBitfields ConstantArrayTypeBits;
AttributedTypeBitfields AttributedTypeBits;
AutoTypeBitfields AutoTypeBits;
+ TypeOfBitfields TypeOfBits;
BuiltinTypeBitfields BuiltinTypeBits;
FunctionTypeBitfields FunctionTypeBits;
ObjCObjectTypeBitfields ObjCObjectTypeBits;
@@ -4527,18 +4547,22 @@
}
};
-/// 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;
protected:
friend class ASTContext; // ASTContext creates these.
- TypeOfExprType(Expr *E, QualType can = QualType());
+ TypeOfExprType(Expr *E, TypeOfKind Kind, QualType Can = QualType());
public:
Expr *getUnderlyingExpr() const { return TOExpr; }
+ /// Returns true if this is a typeof_unqual type.
+ bool isUnqual() const { return TypeOfBits.IsUnqual; }
+
/// Remove a single level of sugar.
QualType desugar() const;
@@ -4559,37 +4583,47 @@
const ASTContext &Context;
public:
- DependentTypeOfExprType(const ASTContext &Context, Expr *E)
- : TypeOfExprType(E), Context(Context) {}
+ DependentTypeOfExprType(const ASTContext &Context, Expr *E, TypeOfKind Kind)
+ : TypeOfExprType(E, Kind), 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;
- TypeOfType(QualType T, QualType can)
- : Type(TypeOf, can, T->getDependence()), TOType(T) {
- assert(!isa<TypedefType>(can) && "Invalid canonical type");
+ TypeOfType(QualType T, QualType Can, TypeOfKind Kind)
+ : Type(TypeOf,
+ Kind == TypeOfKind::Unqualified ? Can.getTypeofUnqualType() : Can,
+ T->getDependence()),
+ TOType(T) {
+ TypeOfBits.IsUnqual = Kind == TypeOfKind::Unqualified;
}
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 TypeOfBits.IsUnqual; }
+
static bool classof(const Type *T) { return T->getTypeClass() == TypeOf; }
};
Index: clang/include/clang/AST/PropertiesBase.td
===================================================================
--- clang/include/clang/AST/PropertiesBase.td
+++ clang/include/clang/AST/PropertiesBase.td
@@ -138,6 +138,7 @@
def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">;
def TemplateName : DefaultValuePropertyType;
def TemplateNameKind : EnumPropertyType<"TemplateName::NameKind">;
+def TypeOfKind : EnumPropertyType<"TypeOfKind">;
def UInt32 : CountPropertyType<"uint32_t">;
def UInt64 : CountPropertyType<"uint64_t">;
def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -1714,9 +1714,9 @@
/// Return a ObjCObjectPointerType type for the given ObjCObjectType.
QualType getObjCObjectPointerType(QualType OIT) const;
- /// GCC extension.
- QualType getTypeOfExprType(Expr *e) const;
- QualType getTypeOfType(QualType t) const;
+ /// C2x feature and GCC extension.
+ QualType getTypeOfExprType(Expr *E, TypeOfKind Kind) const;
+ QualType getTypeOfType(QualType QT, TypeOfKind Kind) const;
QualType getReferenceQualifiedType(const Expr *e) const;
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -282,6 +282,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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits