aaron.ballman created this revision.
aaron.ballman added reviewers: clang-language-wg, efriedma, jyknight,
hubert.reinterpretcast, rsmith.
Herald added a subscriber: martong.
Herald added a reviewer: shafik.
Herald added a reviewer: NoQ.
Herald added a project: All.
aaron.ballman requested review of this revision.
Herald added a project: clang.
Extends `_Generic` selection expressions to accept a type operand in addition
to an expression operand. The type operand form does not perform lvalue
conversions on the operand, which allows the expression to select *qualified*
types, making it more useful for generic programming in C.
C has a few operators that take either a type or an expression, such as
`sizeof`. It is natural to extend that idea to `_Generic` so that it can also
accept a type for the first operand. This type does not undergo any
conversions, which allows it to match qualified types, incomplete types, and
function types. C2x has the `typeof` operator as a way to get the type of an
expression before lvalue conversion takes place, and so it keeps the
qualification. This makes `typeof` a straightforward approach to determining a
type operand for `_Generic` that considers qualifiers. This allows writing a
helper macro like:
#define EXPR_HAS_TYPE(Expr, Type) _Generic(typeof(Expr), Type : 1, default :
0)
which can be called with an expression of any value category (no need to be an
lvalue) and will test against (almost) any type. This is a conforming extension
to C (it's defining the behavior of invalid syntax), and I believe this is a
natural way to extend this functionality.
`_Generic` with a type operand will relax the requirements of what can be a
valid association. Specifically, it allows incomplete types and non-object
types (but still prevents use of variably-modified types). This relaxation only
happens for the type operand form; the expression operand form continues to
behave as it always has.
This extension allows incomplete and non-object types because the goal is to
better enable type-generic programming in C, and so we want to allow any typed
construct we can statically determine the type of. There is no reason to
prevent matching against `void` or function types, but this does explain why we
continue to prohibit variably-modified types.
Please see the RFC at
https://discourse.llvm.org/t/rfc-generic-selection-expression-with-a-type-operand/70388
for more details.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D149904
Files:
clang/include/clang/AST/ASTNodeTraverser.h
clang/include/clang/AST/Expr.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ComputeDependence.cpp
clang/lib/AST/Expr.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/Analysis/ExprMutationAnalyzer.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Parse/ParseTentative.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprObjC.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaPseudoObject.cpp
clang/lib/Sema/TreeTransform.h
clang/test/Parser/generic-selection-type-extension-pedantic.c
clang/test/Parser/generic-selection-type-extension.c
clang/test/Sema/generic-selection-type-extension.c
clang/test/SemaCXX/generic-selection.cpp
Index: clang/test/SemaCXX/generic-selection.cpp
===================================================================
--- clang/test/SemaCXX/generic-selection.cpp
+++ clang/test/SemaCXX/generic-selection.cpp
@@ -3,7 +3,7 @@
template <typename T, typename U = void*>
struct A {
enum {
- id = _Generic(T(), // expected-error {{controlling expression type 'char' not compatible with any generic association type}}
+ id = _Generic(T{}, // expected-error {{controlling expression type 'char' not compatible with any generic association type}}
int: 1, // expected-note {{compatible type 'int' specified here}}
float: 2,
U: 3) // expected-error {{type 'int' in generic association compatible with previously specified type 'int'}}
@@ -20,7 +20,7 @@
template <typename T, typename U>
struct B {
enum {
- id = _Generic(T(),
+ id = _Generic(T{},
int: 1, // expected-note {{compatible type 'int' specified here}}
int: 2, // expected-error {{type 'int' in generic association compatible with previously specified type 'int'}}
U: 3)
@@ -37,7 +37,7 @@
template <class... Args> struct TypeMask {
enum {
- result = Or<_Generic(Args(), int: 1, long: 2, short: 4, float: 8)...>::result
+ result = Or<_Generic(Args{}, int: 1, long: 2, short: 4, float: 8)...>::result
};
};
Index: clang/test/Sema/generic-selection-type-extension.c
===================================================================
--- /dev/null
+++ clang/test/Sema/generic-selection-type-extension.c
@@ -0,0 +1,130 @@
+// RUN: %clang_cc1 -std=c2x -fsyntax-only -verify -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused -x c++ %s
+
+// Test that the semantic behavior of the extension allowing the user to pass a
+// type as the first argument to _Generic.
+
+// Test that we match on basic types.
+static_assert(_Generic(int, int : 1, default : 0) == 1);
+static_assert(_Generic(_BitInt(12), int : 1, _BitInt(10) : 2, _BitInt(12) : 3) == 3);
+
+// Test that we correctly fall back to the default association appropriately.
+static_assert(_Generic(int, long : 1, default : 0) == 0);
+
+// Ensure we correctly match constant arrays by their extent.
+static_assert(_Generic(int[12], int[0] : 0, int * : 0, int[12] : 1, default : 0) == 1);
+
+// Ensure we correctly match function types by their signature.
+static_assert(_Generic(int(int), void(void) : 0, int(void) : 0, void(int) : 0, int(int) : 1, default : 0) == 1);
+
+// Test that we still diagnose when no associations match and that the
+// diagnostic includes qualifiers.
+static_assert(_Generic(const int, long : 1)); // expected-error {{controlling expression type 'const int' not compatible with any generic association type}}
+
+// Test that qualifiers work as expected and do not issue a diagnostic when
+// using the type form.
+static_assert(_Generic(const int, int : 0, const int : 1) == 1);
+static_assert(_Generic(int volatile _Atomic const, int : 0, const int : 0, volatile int : 0, _Atomic int : 0, _Atomic const volatile int : 1) == 1);
+
+// Test that inferred qualifiers also work as expected.
+const int ci = 0;
+static_assert(_Generic(__typeof__(ci), int : 0, const int : 1) == 1);
+// And that the expression form still complains about qualified associations
+// and matches the correct association.
+static_assert(_Generic(ci, int : 1, const int : 0) == 1); // expected-warning {{due to lvalue conversion of the controlling expression, association of type 'const int' will never be selected because it is qualified}}
+
+// The type operand form of _Generic allows incomplete and non-object types,
+// but the expression operand form still rejects them.
+static_assert(_Generic(struct incomplete, struct incomplete : 1, default : 0) == 1);
+static_assert(_Generic(struct another_incomplete, struct incomplete : 1, default : 0) == 0);
+static_assert(_Generic(1, struct also_incomplete : 1, default : 0) == 0); // expected-error {{type 'struct also_incomplete' in generic association incomplete}}
+
+void foo(int);
+static_assert(_Generic(__typeof__(foo), void(int) : 1, default : 0) == 1);
+static_assert(_Generic(foo, void(int) : 1, default : 0) == 0); // expected-error {{type 'void (int)' in generic association not an object type}}
+
+// Ensure we still get a diagnostic for duplicated associations for the type
+// form, even when using qualified type, and that the diagnostic includes
+// qualifiers.
+static_assert(_Generic(const int,
+ const int : 1, // expected-note {{compatible type 'const int' specified here}}
+ int : 2,
+ const int : 3 // expected-error {{type 'const int' in generic association compatible with previously specified type 'const int'}}
+ ) == 1);
+
+// Verify that we are matching using the canonical type of the type operand...
+typedef int Int;
+typedef const Int CInt;
+typedef CInt OtherCInt;
+static_assert(_Generic(volatile CInt, const volatile int : 1, default : 0) == 1);
+static_assert(_Generic(const int, CInt : 1, default : 0) == 1);
+
+// ...and that duplicate associations are doing so as well.
+static_assert(_Generic(const int,
+ CInt : 1, // expected-note {{compatible type 'CInt' (aka 'const int') specified here}}
+ const volatile int : 2,
+ OtherCInt : 3 // expected-error {{type 'OtherCInt' (aka 'const int') in generic association compatible with previously specified type 'CInt' (aka 'const int')}}
+ ) == 1);
+
+// Also test that duplicate array or function types are caught.
+static_assert(_Generic(const int,
+ int[12] : 0, // expected-note {{compatible type 'int[12]' specified here}}
+ int[12] : 0, // expected-error {{type 'int[12]' in generic association compatible with previously specified type 'int[12]'}}
+ int(int) : 0, // expected-note {{compatible type 'int (int)' specified here}}
+ int(int) : 0, // expected-error {{type 'int (int)' in generic association compatible with previously specified type 'int (int)'}}
+ default : 1
+ ) == 1);
+
+
+// Tests that only make sense for C++:
+#ifdef __cplusplus
+// Ensure that _Generic works within a template argument list.
+template <typename Ty, int N = _Generic(Ty, int : 0, default : 1)>
+constexpr Ty bar() { return N; }
+
+static_assert(bar<int>() == 0);
+static_assert(bar<float>() == 1);
+
+// Or that it can be used as a non-type template argument.
+static_assert(bar<int, _Generic(int, int : 1, default : 0)>() == 1);
+
+// Ensure that a dependent type works as expected.
+template <typename Ty>
+struct Dependent {
+ // If we checked the type early, this would fail to compile without any
+ // instantiation. Instead, it only fails with the bad instantiation.
+ static_assert(_Generic(Ty, int : 1)); // expected-error {{controlling expression type 'double' not compatible with any generic association type}} \
+ expected-note@#BadInstantiation {{in instantiation of template class 'Dependent<double>' requested here}}
+};
+
+template struct Dependent<int>; // Good instantiation
+template struct Dependent<double>; // #BadInstantiation
+
+// Another template instantiation test, this time for a variable template with
+// a type-dependent initializer.
+template <typename Ty>
+constexpr auto Val = _Generic(Ty, Ty : Ty{});
+
+static_assert(Val<int> == 0);
+static_assert(__is_same(decltype(Val<Dependent<int>>), const Dependent<int>));
+
+// Ensure that pack types also work as expected.
+template <unsigned Arg, unsigned... Args> struct Or {
+ enum { result = Arg | Or<Args...>::result };
+};
+
+template <unsigned Arg> struct Or<Arg> {
+ enum { result = Arg };
+};
+
+template <class... Args> struct TypeMask {
+ enum {
+ result = Or<_Generic(Args, int: 1, long: 2, short: 4, float: 8)...>::result
+ };
+};
+
+static_assert(TypeMask<int, long, short>::result == 7, "fail");
+static_assert(TypeMask<float, short>::result == 12, "fail");
+static_assert(TypeMask<int, float, float>::result == 9, "fail");
+#endif // __cplusplus
+
Index: clang/test/Parser/generic-selection-type-extension.c
===================================================================
--- /dev/null
+++ clang/test/Parser/generic-selection-type-extension.c
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c2x -fsyntax-only -verify -Wno-unused %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cpp -Wno-unused -x c++ %s
+
+// Test various parsing situations for the Clang extension to _Generic which
+// accepts a type name instead of an expression as the first operand.
+
+int foo();
+
+void test() {
+ // We can parse a simple type name.
+ _Generic(int, int : 0);
+
+ // We can also parse tag types.
+ struct S { int i; };
+ enum E { A };
+ union U { int i; };
+ _Generic(struct S, default : 0);
+ _Generic(enum E, default : 0);
+ _Generic(union U, default : 0);
+
+ // We can also parse array types.
+ _Generic(int[12], default : 0);
+
+ // And pointer to array types, too.
+ _Generic(int(*)[12], default : 0);
+
+ // We do not accept a parenthesized type name.
+ _Generic((int), int : 0); // expected-error {{expected expression}}
+
+ // We can parse more complex types as well. Note, this is a valid spelling of
+ // a function pointer type in C but is not a valid spelling of a function
+ // pointer type in C++. Surprise!
+ _Generic(__typeof__(foo())(*)(__typeof__(&foo)), int (*)(int (*)()) : 0); // cpp-error {{expected expression}} \
+ cpp-error {{expected '(' for function-style cast or type construction}}
+
+ // C being the magical language that it is, lets you define a type anywhere
+ // you can spell a type.
+ _Generic(struct T { int a; }, default : 0); // cpp-error {{'T' cannot be defined in a type specifier}}
+}
+
+#ifdef __cplusplus
+template <typename Ty>
+struct S {
+ template <template <typename> typename Uy>
+ struct T {
+ typedef typename Uy<Ty>::type foo;
+ };
+};
+
+template <typename Ty>
+struct inst {
+ typedef Ty type;
+};
+
+void cpp_test() {
+ // Ensure we can parse more complex C++ typenames as well.
+ _Generic(S<int>::T<inst>::foo, int : 1);
+
+ // And that the type name doesn't confuse us when given an initialization
+ // expression.
+ _Generic(S<int>::T<inst>::foo{}, int : 1);
+}
+
+template <typename Ty, int N = _Generic(Ty, int : 0, default : 1)>
+constexpr Ty bar() { return N; }
+
+static_assert(bar<int>() == 0);
+static_assert(bar<float>() == 1);
+#endif // __cplusplus
Index: clang/test/Parser/generic-selection-type-extension-pedantic.c
===================================================================
--- /dev/null
+++ clang/test/Parser/generic-selection-type-extension-pedantic.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c2x -fsyntax-only -verify -pedantic %s
+
+// Test that we get the extension warning when appropriate and that it shows up
+// in the right location.
+void test(void) {
+ (void)_Generic(
+ int, // expected-warning {{passing a type argument as the first operand to '_Generic' is a Clang extension}}
+ int : 0);
+ (void)_Generic(
+ 12,
+ int : 0);
+}
+
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -3014,7 +3014,7 @@
RParenLoc);
}
- /// Build a new generic selection expression.
+ /// Build a new generic selection expression with an expression predicate.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
@@ -3025,9 +3025,25 @@
ArrayRef<TypeSourceInfo *> Types,
ArrayRef<Expr *> Exprs) {
return getSema().CreateGenericSelectionExpr(KeyLoc, DefaultLoc, RParenLoc,
+ /*PredicateIsExpr=*/true,
ControllingExpr, Types, Exprs);
}
+ /// Build a new generic selection expression with a type predicate.
+ ///
+ /// By default, performs semantic analysis to build the new expression.
+ /// Subclasses may override this routine to provide different behavior.
+ ExprResult RebuildGenericSelectionExpr(SourceLocation KeyLoc,
+ SourceLocation DefaultLoc,
+ SourceLocation RParenLoc,
+ TypeSourceInfo *ControllingType,
+ ArrayRef<TypeSourceInfo *> Types,
+ ArrayRef<Expr *> Exprs) {
+ return getSema().CreateGenericSelectionExpr(KeyLoc, DefaultLoc, RParenLoc,
+ /*PredicateIsExpr=*/false,
+ ControllingType, Types, Exprs);
+ }
+
/// Build a new overloaded operator call expression.
///
/// By default, performs semantic analysis to build the new expression.
@@ -10853,9 +10869,14 @@
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformGenericSelectionExpr(GenericSelectionExpr *E) {
- ExprResult ControllingExpr =
- getDerived().TransformExpr(E->getControllingExpr());
- if (ControllingExpr.isInvalid())
+ ExprResult ControllingExpr;
+ TypeSourceInfo *ControllingType = nullptr;
+ if (E->isExprPredicate())
+ ControllingExpr = getDerived().TransformExpr(E->getControllingExpr());
+ else
+ ControllingType = getDerived().TransformType(E->getControllingType());
+
+ if (ControllingExpr.isInvalid() && !ControllingType)
return ExprError();
SmallVector<Expr *, 4> AssocExprs;
@@ -10878,12 +10899,16 @@
AssocExprs.push_back(AssocExpr.get());
}
+ if (!ControllingType)
return getDerived().RebuildGenericSelectionExpr(E->getGenericLoc(),
E->getDefaultLoc(),
E->getRParenLoc(),
ControllingExpr.get(),
AssocTypes,
AssocExprs);
+ return getDerived().RebuildGenericSelectionExpr(
+ E->getGenericLoc(), E->getDefaultLoc(), E->getRParenLoc(),
+ ControllingType, AssocTypes, AssocExprs);
}
template<typename Derived>
Index: clang/lib/Sema/SemaPseudoObject.cpp
===================================================================
--- clang/lib/Sema/SemaPseudoObject.cpp
+++ clang/lib/Sema/SemaPseudoObject.cpp
@@ -152,8 +152,13 @@
assocTypes.push_back(assoc.getTypeSourceInfo());
}
+ if (gse->isExprPredicate())
+ return GenericSelectionExpr::Create(
+ S.Context, gse->getGenericLoc(), gse->getControllingExpr(),
+ assocTypes, assocExprs, gse->getDefaultLoc(), gse->getRParenLoc(),
+ gse->containsUnexpandedParameterPack(), resultIndex);
return GenericSelectionExpr::Create(
- S.Context, gse->getGenericLoc(), gse->getControllingExpr(),
+ S.Context, gse->getGenericLoc(), gse->getControllingType(),
assocTypes, assocExprs, gse->getDefaultLoc(), gse->getRParenLoc(),
gse->containsUnexpandedParameterPack(), resultIndex);
}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -15465,8 +15465,14 @@
unsigned ResultIdx = GSE->getResultIndex();
AssocExprs[ResultIdx] = SubExpr;
+ if (GSE->isExprPredicate())
+ return GenericSelectionExpr::Create(
+ Context, GSE->getGenericLoc(), GSE->getControllingExpr(),
+ GSE->getAssocTypeSourceInfos(), AssocExprs, GSE->getDefaultLoc(),
+ GSE->getRParenLoc(), GSE->containsUnexpandedParameterPack(),
+ ResultIdx);
return GenericSelectionExpr::Create(
- Context, GSE->getGenericLoc(), GSE->getControllingExpr(),
+ Context, GSE->getGenericLoc(), GSE->getControllingType(),
GSE->getAssocTypeSourceInfos(), AssocExprs, GSE->getDefaultLoc(),
GSE->getRParenLoc(), GSE->containsUnexpandedParameterPack(),
ResultIdx);
Index: clang/lib/Sema/SemaExprObjC.cpp
===================================================================
--- clang/lib/Sema/SemaExprObjC.cpp
+++ clang/lib/Sema/SemaExprObjC.cpp
@@ -4551,6 +4551,7 @@
CurFPFeatureOverrides());
} else if (GenericSelectionExpr *gse = dyn_cast<GenericSelectionExpr>(e)) {
assert(!gse->isResultDependent());
+ assert(!gse->isTypePredicate());
unsigned n = gse->getNumAssocs();
SmallVector<Expr *, 4> subExprs;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -1613,13 +1613,10 @@
//===----------------------------------------------------------------------===//
-ExprResult
-Sema::ActOnGenericSelectionExpr(SourceLocation KeyLoc,
- SourceLocation DefaultLoc,
- SourceLocation RParenLoc,
- Expr *ControllingExpr,
- ArrayRef<ParsedType> ArgTypes,
- ArrayRef<Expr *> ArgExprs) {
+ExprResult Sema::ActOnGenericSelectionExpr(
+ SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc,
+ bool PredicateIsExpr, void *ControllingExprOrType,
+ ArrayRef<ParsedType> ArgTypes, ArrayRef<Expr *> ArgExprs) {
unsigned NumAssocs = ArgTypes.size();
assert(NumAssocs == ArgExprs.size());
@@ -1631,42 +1628,64 @@
Types[i] = nullptr;
}
- ExprResult ER =
- CreateGenericSelectionExpr(KeyLoc, DefaultLoc, RParenLoc, ControllingExpr,
- llvm::ArrayRef(Types, NumAssocs), ArgExprs);
+ // If we have a controlling type, we need to convert it from a parsed type
+ // into a semantic type and then pass that along.
+ if (!PredicateIsExpr) {
+ TypeSourceInfo *ControllingType;
+ (void)GetTypeFromParser(ParsedType::getFromOpaquePtr(ControllingExprOrType),
+ &ControllingType);
+ assert(ControllingType && "couldn't get the type out of the parser");
+ ControllingExprOrType = ControllingType;
+ }
+
+ ExprResult ER = CreateGenericSelectionExpr(
+ KeyLoc, DefaultLoc, RParenLoc, PredicateIsExpr, ControllingExprOrType,
+ llvm::ArrayRef(Types, NumAssocs), ArgExprs);
delete [] Types;
return ER;
}
-ExprResult
-Sema::CreateGenericSelectionExpr(SourceLocation KeyLoc,
- SourceLocation DefaultLoc,
- SourceLocation RParenLoc,
- Expr *ControllingExpr,
- ArrayRef<TypeSourceInfo *> Types,
- ArrayRef<Expr *> Exprs) {
+ExprResult Sema::CreateGenericSelectionExpr(
+ SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc,
+ bool PredicateIsExpr, void *ControllingExprOrType,
+ ArrayRef<TypeSourceInfo *> Types, ArrayRef<Expr *> Exprs) {
unsigned NumAssocs = Types.size();
assert(NumAssocs == Exprs.size());
-
- // Decay and strip qualifiers for the controlling expression type, and handle
- // placeholder type replacement. See committee discussion from WG14 DR423.
- {
+ assert(ControllingExprOrType &&
+ "Must have either a controlling expression or a controlling type");
+
+ Expr *ControllingExpr = nullptr;
+ TypeSourceInfo *ControllingType = nullptr;
+ if (PredicateIsExpr) {
+ // Decay and strip qualifiers for the controlling expression type, and
+ // handle placeholder type replacement. See committee discussion from WG14
+ // DR423.
EnterExpressionEvaluationContext Unevaluated(
*this, Sema::ExpressionEvaluationContext::Unevaluated);
- ExprResult R = DefaultFunctionArrayLvalueConversion(ControllingExpr);
+ ExprResult R = DefaultFunctionArrayLvalueConversion(
+ reinterpret_cast<Expr *>(ControllingExprOrType));
if (R.isInvalid())
return ExprError();
ControllingExpr = R.get();
+ } else {
+ // The extension form uses the type directly rather than converting it.
+ ControllingType = reinterpret_cast<TypeSourceInfo *>(ControllingExprOrType);
+ if (!ControllingType)
+ return ExprError();
}
bool TypeErrorFound = false,
- IsResultDependent = ControllingExpr->isTypeDependent(),
- ContainsUnexpandedParameterPack
- = ControllingExpr->containsUnexpandedParameterPack();
+ IsResultDependent = ControllingExpr
+ ? ControllingExpr->isTypeDependent()
+ : ControllingType->getType()->isDependentType(),
+ ContainsUnexpandedParameterPack =
+ ControllingExpr
+ ? ControllingExpr->containsUnexpandedParameterPack()
+ : ControllingType->getType()->containsUnexpandedParameterPack();
// The controlling expression is an unevaluated operand, so side effects are
// likely unintended.
- if (!inTemplateInstantiation() && !IsResultDependent &&
+ if (!inTemplateInstantiation() && !IsResultDependent && ControllingExpr &&
ControllingExpr->HasSideEffects(Context, false))
Diag(ControllingExpr->getExprLoc(),
diag::warn_side_effects_unevaluated_context);
@@ -1682,16 +1701,24 @@
if (Types[i]->getType()->isDependentType()) {
IsResultDependent = true;
} else {
+ // We relax the restriction on use of incomplete types and non-object
+ // types with the type-based extension of _Generic. Allowing incomplete
+ // objects means those can be used as "tags" for a type-safe way to map
+ // to a value. Similarly, matching on function types rather than
+ // function pointer types can be useful. However, the restriction on VM
+ // types makes sense to retain as there are open questions about how
+ // the selection can be made at compile time.
+ //
// C11 6.5.1.1p2 "The type name in a generic association shall specify a
// complete object type other than a variably modified type."
unsigned D = 0;
- if (Types[i]->getType()->isIncompleteType())
+ if (ControllingExpr && Types[i]->getType()->isIncompleteType())
D = diag::err_assoc_type_incomplete;
- else if (!Types[i]->getType()->isObjectType())
+ else if (ControllingExpr && !Types[i]->getType()->isObjectType())
D = diag::err_assoc_type_nonobject;
else if (Types[i]->getType()->isVariablyModifiedType())
D = diag::err_assoc_type_variably_modified;
- else {
+ else if (ControllingExpr) {
// Because the controlling expression undergoes lvalue conversion,
// array conversion, and function conversion, an association which is
// of array type, function type, or is qualified can never be
@@ -1706,6 +1733,10 @@
// The result of these rules is that all qualified types in an
// association in C are unreachable, and in C++, only qualified non-
// class types are unreachable.
+ //
+ // NB: this does not apply when the first operand is a type rather
+ // than an expression, because the type form does not undergo
+ // conversion.
unsigned Reason = 0;
QualType QT = Types[i]->getType();
if (QT->isArrayType())
@@ -1752,10 +1783,15 @@
// If we determined that the generic selection is result-dependent, don't
// try to compute the result expression.
- if (IsResultDependent)
- return GenericSelectionExpr::Create(Context, KeyLoc, ControllingExpr, Types,
+ if (IsResultDependent) {
+ if (ControllingExpr)
+ return GenericSelectionExpr::Create(Context, KeyLoc, ControllingExpr,
+ Types, Exprs, DefaultLoc, RParenLoc,
+ ContainsUnexpandedParameterPack);
+ return GenericSelectionExpr::Create(Context, KeyLoc, ControllingType, Types,
Exprs, DefaultLoc, RParenLoc,
ContainsUnexpandedParameterPack);
+ }
SmallVector<unsigned, 1> CompatIndices;
unsigned DefaultIndex = -1U;
@@ -1765,22 +1801,42 @@
for (unsigned i = 0; i < NumAssocs; ++i) {
if (!Types[i])
DefaultIndex = i;
- else if (Context.typesAreCompatible(
+ else if (ControllingExpr &&
+ Context.typesAreCompatible(
ControllingExpr->getType().getCanonicalType(),
- Types[i]->getType()))
+ Types[i]->getType()))
+ CompatIndices.push_back(i);
+ else if (ControllingType &&
+ Context.typesAreCompatible(
+ ControllingType->getType().getCanonicalType(),
+ Types[i]->getType()))
CompatIndices.push_back(i);
}
+ auto GetControllingRangeAndType = [](Expr *ControllingExpr,
+ TypeSourceInfo *ControllingType) {
+ // We strip parens here because the controlling expression is typically
+ // parenthesized in macro definitions.
+ if (ControllingExpr)
+ ControllingExpr = ControllingExpr->IgnoreParens();
+
+ SourceRange SR = ControllingExpr
+ ? ControllingExpr->getSourceRange()
+ : ControllingType->getTypeLoc().getSourceRange();
+ QualType QT = ControllingExpr ? ControllingExpr->getType()
+ : ControllingType->getType();
+
+ return std::make_pair(SR, QT);
+ };
+
// C11 6.5.1.1p2 "The controlling expression of a generic selection shall have
// type compatible with at most one of the types named in its generic
// association list."
if (CompatIndices.size() > 1) {
- // We strip parens here because the controlling expression is typically
- // parenthesized in macro definitions.
- ControllingExpr = ControllingExpr->IgnoreParens();
- Diag(ControllingExpr->getBeginLoc(), diag::err_generic_sel_multi_match)
- << ControllingExpr->getSourceRange() << ControllingExpr->getType()
- << (unsigned)CompatIndices.size();
+ auto P = GetControllingRangeAndType(ControllingExpr, ControllingType);
+ SourceRange SR = P.first;
+ Diag(SR.getBegin(), diag::err_generic_sel_multi_match)
+ << SR << P.second << (unsigned)CompatIndices.size();
for (unsigned I : CompatIndices) {
Diag(Types[I]->getTypeLoc().getBeginLoc(),
diag::note_compat_assoc)
@@ -1794,11 +1850,9 @@
// its controlling expression shall have type compatible with exactly one of
// the types named in its generic association list."
if (DefaultIndex == -1U && CompatIndices.size() == 0) {
- // We strip parens here because the controlling expression is typically
- // parenthesized in macro definitions.
- ControllingExpr = ControllingExpr->IgnoreParens();
- Diag(ControllingExpr->getBeginLoc(), diag::err_generic_sel_no_match)
- << ControllingExpr->getSourceRange() << ControllingExpr->getType();
+ auto P = GetControllingRangeAndType(ControllingExpr, ControllingType);
+ SourceRange SR = P.first;
+ Diag(SR.getBegin(), diag::err_generic_sel_no_match) << SR << P.second;
return ExprError();
}
@@ -1810,8 +1864,13 @@
unsigned ResultIndex =
CompatIndices.size() ? CompatIndices[0] : DefaultIndex;
+ if (ControllingExpr) {
+ return GenericSelectionExpr::Create(
+ Context, KeyLoc, ControllingExpr, Types, Exprs, DefaultLoc, RParenLoc,
+ ContainsUnexpandedParameterPack, ResultIndex);
+ }
return GenericSelectionExpr::Create(
- Context, KeyLoc, ControllingExpr, Types, Exprs, DefaultLoc, RParenLoc,
+ Context, KeyLoc, ControllingType, Types, Exprs, DefaultLoc, RParenLoc,
ContainsUnexpandedParameterPack, ResultIndex);
}
@@ -19787,9 +19846,15 @@
}
}
+ void *ExOrTy = nullptr;
+ bool IsExpr = GSE->isExprPredicate();
+ if (IsExpr)
+ ExOrTy = GSE->getControllingExpr();
+ else
+ ExOrTy = GSE->getControllingType();
return AnyChanged ? S.CreateGenericSelectionExpr(
GSE->getGenericLoc(), GSE->getDefaultLoc(),
- GSE->getRParenLoc(), GSE->getControllingExpr(),
+ GSE->getRParenLoc(), IsExpr, ExOrTy,
GSE->getAssocTypeSourceInfos(), AssocExprs)
: ExprEmpty();
}
Index: clang/lib/Parse/ParseTentative.cpp
===================================================================
--- clang/lib/Parse/ParseTentative.cpp
+++ clang/lib/Parse/ParseTentative.cpp
@@ -642,7 +642,12 @@
if (Context == TypeIdInParens && Tok.is(tok::r_paren)) {
TPR = TPResult::True;
isAmbiguous = true;
-
+ // We are supopsed to be inside the first operand to a _Generic selection
+ // expression, so if we find a comma after the declarator, we've found a
+ // type and not an expression.
+ } else if (Context == TypeIdAsGenericSelectionArgument && Tok.is(tok::comma)) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
// We are supposed to be inside a template argument, so if after
// the abstract declarator we encounter a '>', '>>' (in C++0x), or
// ','; or, in C++0x, an ellipsis immediately preceding such, this
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ clang/lib/Parse/ParseExpr.cpp
@@ -3280,6 +3280,12 @@
/// type-name : assignment-expression
/// default : assignment-expression
/// \endverbatim
+///
+/// As an extension, Clang also accepts:
+/// \verbatim
+/// generic-selection:
+/// _Generic ( type-name, generic-assoc-list )
+/// \endverbatim
ExprResult Parser::ParseGenericSelectionExpression() {
assert(Tok.is(tok::kw__Generic) && "_Generic keyword expected");
if (!getLangOpts().C11)
@@ -3290,8 +3296,21 @@
if (T.expectAndConsume())
return ExprError();
+ // We either have a controlling expression or we have a controlling type, and
+ // we need to figure out which it is.
+ TypeResult ControllingType;
ExprResult ControllingExpr;
- {
+ if (isTypeIdForGenericSelection()) {
+ ControllingType = ParseTypeName();
+ if (ControllingType.isInvalid()) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ } else {
+ const auto *LIT = cast<LocInfoType>(ControllingType.get().get());
+ SourceLocation Loc = LIT->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
+ Diag(Loc, diag::ext_generic_with_type_arg);
+ }
+ } else {
// C11 6.5.1.1p3 "The controlling expression of a generic selection is
// not evaluated."
EnterExpressionEvaluationContext Unevaluated(
@@ -3356,10 +3375,13 @@
if (T.getCloseLocation().isInvalid())
return ExprError();
- return Actions.ActOnGenericSelectionExpr(KeyLoc, DefaultLoc,
- T.getCloseLocation(),
- ControllingExpr.get(),
- Types, Exprs);
+ void *ExprOrTy = ControllingExpr.isUsable()
+ ? ControllingExpr.get()
+ : ControllingType.get().getAsOpaquePtr();
+
+ return Actions.ActOnGenericSelectionExpr(
+ KeyLoc, DefaultLoc, T.getCloseLocation(), ControllingExpr.isUsable(),
+ ExprOrTy, Types, Exprs);
}
/// Parse A C++1z fold-expression after the opening paren and optional
Index: clang/lib/Analysis/ExprMutationAnalyzer.cpp
===================================================================
--- clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -102,6 +102,8 @@
AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ if (Node.isTypePredicate())
+ return false;
return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
}
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -1458,8 +1458,12 @@
void StmtPrinter::VisitGenericSelectionExpr(GenericSelectionExpr *Node) {
OS << "_Generic(";
- PrintExpr(Node->getControllingExpr());
- for (const GenericSelectionExpr::Association Assoc : Node->associations()) {
+ if (Node->isExprPredicate())
+ PrintExpr(Node->getControllingExpr());
+ else
+ Node->getControllingType()->getType().print(OS, Policy);
+
+ for (const GenericSelectionExpr::Association &Assoc : Node->associations()) {
OS << ", ";
QualType T = Assoc.getType();
if (T.isNull())
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -4320,18 +4320,48 @@
AssocExprs[ResultIndex]->getValueKind(),
AssocExprs[ResultIndex]->getObjectKind()),
NumAssocs(AssocExprs.size()), ResultIndex(ResultIndex),
- DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
+ IsExprPredicate(true), DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
assert(AssocTypes.size() == AssocExprs.size() &&
"Must have the same number of association expressions"
" and TypeSourceInfo!");
assert(ResultIndex < NumAssocs && "ResultIndex is out-of-bounds!");
GenericSelectionExprBits.GenericLoc = GenericLoc;
- getTrailingObjects<Stmt *>()[ControllingIndex] = ControllingExpr;
+ getTrailingObjects<Stmt *>()[getIndexOfControllingExpression()] =
+ ControllingExpr;
std::copy(AssocExprs.begin(), AssocExprs.end(),
- getTrailingObjects<Stmt *>() + AssocExprStartIndex);
+ getTrailingObjects<Stmt *>() + getIndexOfStartOfAssociatedExprs());
std::copy(AssocTypes.begin(), AssocTypes.end(),
- getTrailingObjects<TypeSourceInfo *>());
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes());
+
+ setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
+}
+
+GenericSelectionExpr::GenericSelectionExpr(
+ const ASTContext &, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack,
+ unsigned ResultIndex)
+ : Expr(GenericSelectionExprClass, AssocExprs[ResultIndex]->getType(),
+ AssocExprs[ResultIndex]->getValueKind(),
+ AssocExprs[ResultIndex]->getObjectKind()),
+ NumAssocs(AssocExprs.size()), ResultIndex(ResultIndex),
+ IsExprPredicate(false), DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
+ assert(AssocTypes.size() == AssocExprs.size() &&
+ "Must have the same number of association expressions"
+ " and TypeSourceInfo!");
+ assert(ResultIndex < NumAssocs && "ResultIndex is out-of-bounds!");
+
+ GenericSelectionExprBits.GenericLoc = GenericLoc;
+ getTrailingObjects<TypeSourceInfo *>()[getIndexOfControllingType()] =
+ ControllingType;
+ std::copy(AssocExprs.begin(), AssocExprs.end(),
+ getTrailingObjects<Stmt *>() + getIndexOfStartOfAssociatedExprs());
+ std::copy(AssocTypes.begin(), AssocTypes.end(),
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes());
setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
}
@@ -4344,17 +4374,44 @@
: Expr(GenericSelectionExprClass, Context.DependentTy, VK_PRValue,
OK_Ordinary),
NumAssocs(AssocExprs.size()), ResultIndex(ResultDependentIndex),
- DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
+ IsExprPredicate(true), DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
+ assert(AssocTypes.size() == AssocExprs.size() &&
+ "Must have the same number of association expressions"
+ " and TypeSourceInfo!");
+
+ GenericSelectionExprBits.GenericLoc = GenericLoc;
+ getTrailingObjects<Stmt *>()[getIndexOfControllingExpression()] =
+ ControllingExpr;
+ std::copy(AssocExprs.begin(), AssocExprs.end(),
+ getTrailingObjects<Stmt *>() + getIndexOfStartOfAssociatedExprs());
+ std::copy(AssocTypes.begin(), AssocTypes.end(),
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes());
+
+ setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
+}
+
+GenericSelectionExpr::GenericSelectionExpr(
+ const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack)
+ : Expr(GenericSelectionExprClass, Context.DependentTy, VK_PRValue,
+ OK_Ordinary),
+ NumAssocs(AssocExprs.size()), ResultIndex(ResultDependentIndex),
+ IsExprPredicate(false), DefaultLoc(DefaultLoc), RParenLoc(RParenLoc) {
assert(AssocTypes.size() == AssocExprs.size() &&
"Must have the same number of association expressions"
" and TypeSourceInfo!");
GenericSelectionExprBits.GenericLoc = GenericLoc;
- getTrailingObjects<Stmt *>()[ControllingIndex] = ControllingExpr;
+ getTrailingObjects<TypeSourceInfo *>()[getIndexOfControllingType()] =
+ ControllingType;
std::copy(AssocExprs.begin(), AssocExprs.end(),
- getTrailingObjects<Stmt *>() + AssocExprStartIndex);
+ getTrailingObjects<Stmt *>() + getIndexOfStartOfAssociatedExprs());
std::copy(AssocTypes.begin(), AssocTypes.end(),
- getTrailingObjects<TypeSourceInfo *>());
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes());
setDependence(computeDependence(this, ContainsUnexpandedParameterPack));
}
@@ -4390,6 +4447,35 @@
RParenLoc, ContainsUnexpandedParameterPack);
}
+GenericSelectionExpr *GenericSelectionExpr::Create(
+ const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack,
+ unsigned ResultIndex) {
+ unsigned NumAssocs = AssocExprs.size();
+ void *Mem = Context.Allocate(
+ totalSizeToAlloc<Stmt *, TypeSourceInfo *>(1 + NumAssocs, NumAssocs),
+ alignof(GenericSelectionExpr));
+ return new (Mem) GenericSelectionExpr(
+ Context, GenericLoc, ControllingType, AssocTypes, AssocExprs, DefaultLoc,
+ RParenLoc, ContainsUnexpandedParameterPack, ResultIndex);
+}
+
+GenericSelectionExpr *GenericSelectionExpr::Create(
+ const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack) {
+ unsigned NumAssocs = AssocExprs.size();
+ void *Mem = Context.Allocate(
+ totalSizeToAlloc<Stmt *, TypeSourceInfo *>(1 + NumAssocs, NumAssocs),
+ alignof(GenericSelectionExpr));
+ return new (Mem) GenericSelectionExpr(
+ Context, GenericLoc, ControllingType, AssocTypes, AssocExprs, DefaultLoc,
+ RParenLoc, ContainsUnexpandedParameterPack);
+}
+
GenericSelectionExpr *
GenericSelectionExpr::CreateEmpty(const ASTContext &Context,
unsigned NumAssocs) {
Index: clang/lib/AST/ComputeDependence.cpp
===================================================================
--- clang/lib/AST/ComputeDependence.cpp
+++ clang/lib/AST/ComputeDependence.cpp
@@ -653,7 +653,12 @@
: ExprDependence::None;
for (auto *AE : E->getAssocExprs())
D |= AE->getDependence() & ExprDependence::Error;
- D |= E->getControllingExpr()->getDependence() & ExprDependence::Error;
+
+ if (E->isExprPredicate())
+ D |= E->getControllingExpr()->getDependence() & ExprDependence::Error;
+ else
+ D |= toExprDependenceAsWritten(
+ E->getControllingType()->getType()->getDependence());
if (E->isResultDependent())
return D | ExprDependence::TypeValueInstantiation;
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -7028,7 +7028,14 @@
ASTNodeImporter::VisitGenericSelectionExpr(GenericSelectionExpr *E) {
Error Err = Error::success();
auto ToGenericLoc = importChecked(Err, E->getGenericLoc());
- auto *ToControllingExpr = importChecked(Err, E->getControllingExpr());
+ Expr *ToControllingExpr = nullptr;
+ TypeSourceInfo *ToControllingType = nullptr;
+ if (E->isExprPredicate())
+ ToControllingExpr = importChecked(Err, E->getControllingExpr());
+ else
+ ToControllingType = importChecked(Err, E->getControllingType());
+ assert((ToControllingExpr || ToControllingType) &&
+ "Either the controlling expr or type must be nonnull");
auto ToDefaultLoc = importChecked(Err, E->getDefaultLoc());
auto ToRParenLoc = importChecked(Err, E->getRParenLoc());
if (Err)
@@ -7046,14 +7053,26 @@
const ASTContext &ToCtx = Importer.getToContext();
if (E->isResultDependent()) {
+ if (ToControllingExpr) {
+ return GenericSelectionExpr::Create(
+ ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes),
+ llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc,
+ E->containsUnexpandedParameterPack());
+ }
return GenericSelectionExpr::Create(
- ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes),
+ ToCtx, ToGenericLoc, ToControllingType, llvm::ArrayRef(ToAssocTypes),
llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc,
E->containsUnexpandedParameterPack());
}
+ if (ToControllingExpr) {
+ return GenericSelectionExpr::Create(
+ ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes),
+ llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc,
+ E->containsUnexpandedParameterPack(), E->getResultIndex());
+ }
return GenericSelectionExpr::Create(
- ToCtx, ToGenericLoc, ToControllingExpr, llvm::ArrayRef(ToAssocTypes),
+ ToCtx, ToGenericLoc, ToControllingType, llvm::ArrayRef(ToAssocTypes),
llvm::ArrayRef(ToAssocExprs), ToDefaultLoc, ToRParenLoc,
E->containsUnexpandedParameterPack(), E->getResultIndex());
}
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -5688,16 +5688,21 @@
ExprResult ActOnStringLiteral(ArrayRef<Token> StringToks,
Scope *UDLScope = nullptr);
+ /// ControllingExprOrType is either an opaque pointer coming out of a
+ /// ParsedType or an Expr *.
ExprResult ActOnGenericSelectionExpr(SourceLocation KeyLoc,
SourceLocation DefaultLoc,
SourceLocation RParenLoc,
- Expr *ControllingExpr,
+ bool PredicateIsExpr,
+ void *ControllingExprOrType,
ArrayRef<ParsedType> ArgTypes,
ArrayRef<Expr *> ArgExprs);
+ /// ControllingExprOrType is either a TypeSourceInfo * or an Expr *.
ExprResult CreateGenericSelectionExpr(SourceLocation KeyLoc,
SourceLocation DefaultLoc,
SourceLocation RParenLoc,
- Expr *ControllingExpr,
+ bool PredicateIsExpr,
+ void *ControllingExprOrType,
ArrayRef<TypeSourceInfo *> Types,
ArrayRef<Expr *> Exprs);
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -2521,7 +2521,8 @@
enum TentativeCXXTypeIdContext {
TypeIdInParens,
TypeIdUnambiguous,
- TypeIdAsTemplateArgument
+ TypeIdAsTemplateArgument,
+ TypeIdAsGenericSelectionArgument,
};
@@ -2539,6 +2540,20 @@
return isTypeIdInParens(isAmbiguous);
}
+ /// Checks whether the current tokens form a type-id or an expression for the
+ /// purposes of use as the initial operand to a generic selection expression.
+ /// This requires special handling in C++ because it accepts either a type or
+ /// an expression, and we need to disambiguate which is which. However, we
+ /// cannot use the same logic as we've used for sizeof expressions, because
+ /// that logic relies on the operator only accepting a single argument,
+ /// whereas _Generic accepts a list of arguments.
+ bool isTypeIdForGenericSelection() {
+ bool isAmbiguous;
+ if (getLangOpts().CPlusPlus)
+ return isCXXTypeId(TypeIdAsGenericSelectionArgument, isAmbiguous);
+ return isTypeSpecifierQualifier();
+ }
+
/// Checks if the current tokens form type-id or expression.
/// It is similar to isTypeIdInParens but does not suppose that type-id
/// is in parenthesis.
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -157,6 +157,9 @@
"duplicate default generic association">;
def note_previous_default_assoc : Note<
"previous default generic association is here">;
+def ext_generic_with_type_arg : Extension<
+ "passing a type argument as the first operand to '_Generic' is a Clang "
+ "extension">, InGroup<DiagGroup<"generic-type-extension">>;
def ext_c99_feature : Extension<
"'%0' is a C99 extension">, InGroup<C99>;
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2553,7 +2553,11 @@
// are interleaved. We also need to watch out for null types (default
// generic associations).
DEF_TRAVERSE_STMT(GenericSelectionExpr, {
- TRY_TO(TraverseStmt(S->getControllingExpr()));
+ if (S->isExprPredicate())
+ TRY_TO(TraverseStmt(S->getControllingExpr()));
+ else
+ TRY_TO(TraverseTypeLoc(S->getControllingType()->getTypeLoc()));
+
for (const GenericSelectionExpr::Association Assoc : S->associations()) {
if (TypeSourceInfo *TSI = Assoc.getTypeSourceInfo())
TRY_TO(TraverseTypeLoc(TSI->getTypeLoc()));
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -5665,6 +5665,12 @@
/// which names a dependent type in its association list is result-dependent,
/// which means that the choice of result expression is dependent.
/// Result-dependent generic associations are both type- and value-dependent.
+///
+/// We also allow an extended form in both C and C++ where the controlling
+/// predicate for the selection expression is a type rather than an expression.
+/// This type argument form does not perform any conversions for the
+/// controlling type, which makes it suitable for use with qualified type
+/// associations, which is not possible with the expression form.
class GenericSelectionExpr final
: public Expr,
private llvm::TrailingObjects<GenericSelectionExpr, Stmt *,
@@ -5677,31 +5683,65 @@
/// expression in the case where the generic selection expression is not
/// result-dependent. The result index is equal to ResultDependentIndex
/// if and only if the generic selection expression is result-dependent.
- unsigned NumAssocs, ResultIndex;
+ unsigned NumAssocs, ResultIndex, IsExprPredicate;
enum : unsigned {
ResultDependentIndex = std::numeric_limits<unsigned>::max(),
- ControllingIndex = 0,
- AssocExprStartIndex = 1
};
+ unsigned getIndexOfControllingExpression() const {
+ // If controlled by an expression, the first offset into the Stmt *
+ // trailing array is the controlling expression, the associated expressions
+ // follow this.
+ assert(isExprPredicate() && "Asking for the controlling expression of a "
+ "selection expr predicated by a type");
+ return 0;
+ }
+
+ unsigned getIndexOfControllingType() const {
+ // If controlled by a type, the first offset into the TypeSourceInfo *
+ // trailing array is the controlling type, the associated types follow this.
+ assert(isTypePredicate() && "Asking for the controlling type of a "
+ "selection expr predicated by an expression");
+ return 0;
+ }
+
+ unsigned getIndexOfStartOfAssociatedExprs() const {
+ // If the predicate is a type, then the associated expressions are the only
+ // Stmt * in the trailing array, otherwise we need to offset past the
+ // predicate expression.
+ return (int)isExprPredicate();
+ }
+
+ unsigned getIndexOfStartOfAssociatedTypes() const {
+ // If the predicate is a type, then the associated types follow it in the
+ // trailing array. Otherwise, the associated types are the only
+ // TypeSourceInfo * in the trailing array.
+ return (int)isTypePredicate();
+ }
+
+
/// The location of the "default" and of the right parenthesis.
SourceLocation DefaultLoc, RParenLoc;
// GenericSelectionExpr is followed by several trailing objects.
// They are (in order):
//
- // * A single Stmt * for the controlling expression.
+ // * A single Stmt * for the controlling expression or a TypeSourceInfo * for
+ // the controlling type, depending on the result of isTypePredicate() or
+ // isExprPredicate().
// * An array of getNumAssocs() Stmt * for the association expressions.
// * An array of getNumAssocs() TypeSourceInfo *, one for each of the
// association expressions.
unsigned numTrailingObjects(OverloadToken<Stmt *>) const {
// Add one to account for the controlling expression; the remainder
// are the associated expressions.
- return 1 + getNumAssocs();
+ return getNumAssocs() + (int)isExprPredicate();
}
unsigned numTrailingObjects(OverloadToken<TypeSourceInfo *>) const {
- return getNumAssocs();
+ // Add one to account for the controlling type predicate, the remainder
+ // are the associated types.
+ return getNumAssocs() + (int)isTypePredicate();
}
template <bool Const> class AssociationIteratorTy;
@@ -5782,7 +5822,8 @@
bool operator==(AssociationIteratorTy Other) const { return E == Other.E; }
}; // class AssociationIterator
- /// Build a non-result-dependent generic selection expression.
+ /// Build a non-result-dependent generic selection expression accepting an
+ /// expression predicate.
GenericSelectionExpr(const ASTContext &Context, SourceLocation GenericLoc,
Expr *ControllingExpr,
ArrayRef<TypeSourceInfo *> AssocTypes,
@@ -5791,7 +5832,8 @@
bool ContainsUnexpandedParameterPack,
unsigned ResultIndex);
- /// Build a result-dependent generic selection expression.
+ /// Build a result-dependent generic selection expression accepting an
+ /// expression predicate.
GenericSelectionExpr(const ASTContext &Context, SourceLocation GenericLoc,
Expr *ControllingExpr,
ArrayRef<TypeSourceInfo *> AssocTypes,
@@ -5799,11 +5841,31 @@
SourceLocation RParenLoc,
bool ContainsUnexpandedParameterPack);
+ /// Build a non-result-dependent generic selection expression accepting a
+ /// type predicate.
+ GenericSelectionExpr(const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType,
+ ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc,
+ bool ContainsUnexpandedParameterPack,
+ unsigned ResultIndex);
+
+ /// Build a result-dependent generic selection expression accepting a type
+ /// predicate.
+ GenericSelectionExpr(const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType,
+ ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc,
+ bool ContainsUnexpandedParameterPack);
+
/// Build an empty generic selection expression for deserialization.
explicit GenericSelectionExpr(EmptyShell Empty, unsigned NumAssocs);
public:
- /// Create a non-result-dependent generic selection expression.
+ /// Create a non-result-dependent generic selection expression accepting an
+ /// expression predicate.
static GenericSelectionExpr *
Create(const ASTContext &Context, SourceLocation GenericLoc,
Expr *ControllingExpr, ArrayRef<TypeSourceInfo *> AssocTypes,
@@ -5811,13 +5873,31 @@
SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack,
unsigned ResultIndex);
- /// Create a result-dependent generic selection expression.
+ /// Create a result-dependent generic selection expression accepting an
+ /// expression predicate.
static GenericSelectionExpr *
Create(const ASTContext &Context, SourceLocation GenericLoc,
Expr *ControllingExpr, ArrayRef<TypeSourceInfo *> AssocTypes,
ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack);
+ /// Create a non-result-dependent generic selection expression accepting a
+ /// type predicate.
+ static GenericSelectionExpr *
+ Create(const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack,
+ unsigned ResultIndex);
+
+ /// Create a result-dependent generic selection expression accepting a type
+ /// predicate
+ static GenericSelectionExpr *
+ Create(const ASTContext &Context, SourceLocation GenericLoc,
+ TypeSourceInfo *ControllingType, ArrayRef<TypeSourceInfo *> AssocTypes,
+ ArrayRef<Expr *> AssocExprs, SourceLocation DefaultLoc,
+ SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack);
+
/// Create an empty generic selection expression for deserialization.
static GenericSelectionExpr *CreateEmpty(const ASTContext &Context,
unsigned NumAssocs);
@@ -5845,32 +5925,56 @@
/// Whether this generic selection is result-dependent.
bool isResultDependent() const { return ResultIndex == ResultDependentIndex; }
+ /// Whether this generic selection uses an expression as its controlling
+ /// argument.
+ bool isExprPredicate() const { return IsExprPredicate; }
+ /// Whether this generic selection uses a type as its controlling argument.
+ bool isTypePredicate() const { return !IsExprPredicate; }
+
/// Return the controlling expression of this generic selection expression.
+ /// Only valid to call if the selection expression used an expression as its
+ /// controlling argument.
Expr *getControllingExpr() {
- return cast<Expr>(getTrailingObjects<Stmt *>()[ControllingIndex]);
+ return cast<Expr>(
+ getTrailingObjects<Stmt *>()[getIndexOfControllingExpression()]);
}
const Expr *getControllingExpr() const {
- return cast<Expr>(getTrailingObjects<Stmt *>()[ControllingIndex]);
+ return cast<Expr>(
+ getTrailingObjects<Stmt *>()[getIndexOfControllingExpression()]);
+ }
+
+ /// Return the controlling type of this generic selection expression. Only
+ /// valid to call if the selection expression used a type as its controlling
+ /// argument.
+ TypeSourceInfo *getControllingType() {
+ return getTrailingObjects<TypeSourceInfo *>()[getIndexOfControllingType()];
+ }
+ const TypeSourceInfo* getControllingType() const {
+ return getTrailingObjects<TypeSourceInfo *>()[getIndexOfControllingType()];
}
/// Return the result expression of this controlling expression. Defined if
/// and only if the generic selection expression is not result-dependent.
Expr *getResultExpr() {
return cast<Expr>(
- getTrailingObjects<Stmt *>()[AssocExprStartIndex + getResultIndex()]);
+ getTrailingObjects<Stmt *>()[getIndexOfStartOfAssociatedExprs() +
+ getResultIndex()]);
}
const Expr *getResultExpr() const {
return cast<Expr>(
- getTrailingObjects<Stmt *>()[AssocExprStartIndex + getResultIndex()]);
+ getTrailingObjects<Stmt *>()[getIndexOfStartOfAssociatedExprs() +
+ getResultIndex()]);
}
ArrayRef<Expr *> getAssocExprs() const {
return {reinterpret_cast<Expr *const *>(getTrailingObjects<Stmt *>() +
- AssocExprStartIndex),
+ getIndexOfStartOfAssociatedExprs()),
NumAssocs};
}
ArrayRef<TypeSourceInfo *> getAssocTypeSourceInfos() const {
- return {getTrailingObjects<TypeSourceInfo *>(), NumAssocs};
+ return {getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes(),
+ NumAssocs};
}
/// Return the Ith association expression with its TypeSourceInfo,
@@ -5879,23 +5983,30 @@
assert(I < getNumAssocs() &&
"Out-of-range index in GenericSelectionExpr::getAssociation!");
return Association(
- cast<Expr>(getTrailingObjects<Stmt *>()[AssocExprStartIndex + I]),
- getTrailingObjects<TypeSourceInfo *>()[I],
+ cast<Expr>(
+ getTrailingObjects<Stmt *>()[getIndexOfStartOfAssociatedExprs() +
+ I]),
+ getTrailingObjects<
+ TypeSourceInfo *>()[getIndexOfStartOfAssociatedTypes() + I],
!isResultDependent() && (getResultIndex() == I));
}
ConstAssociation getAssociation(unsigned I) const {
assert(I < getNumAssocs() &&
"Out-of-range index in GenericSelectionExpr::getAssociation!");
return ConstAssociation(
- cast<Expr>(getTrailingObjects<Stmt *>()[AssocExprStartIndex + I]),
- getTrailingObjects<TypeSourceInfo *>()[I],
+ cast<Expr>(
+ getTrailingObjects<Stmt *>()[getIndexOfStartOfAssociatedExprs() +
+ I]),
+ getTrailingObjects<
+ TypeSourceInfo *>()[getIndexOfStartOfAssociatedTypes() + I],
!isResultDependent() && (getResultIndex() == I));
}
association_range associations() {
AssociationIterator Begin(getTrailingObjects<Stmt *>() +
- AssocExprStartIndex,
- getTrailingObjects<TypeSourceInfo *>(),
+ getIndexOfStartOfAssociatedExprs(),
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes(),
/*Offset=*/0, ResultIndex);
AssociationIterator End(Begin.E + NumAssocs, Begin.TSI + NumAssocs,
/*Offset=*/NumAssocs, ResultIndex);
@@ -5904,8 +6015,9 @@
const_association_range associations() const {
ConstAssociationIterator Begin(getTrailingObjects<Stmt *>() +
- AssocExprStartIndex,
- getTrailingObjects<TypeSourceInfo *>(),
+ getIndexOfStartOfAssociatedExprs(),
+ getTrailingObjects<TypeSourceInfo *>() +
+ getIndexOfStartOfAssociatedTypes(),
/*Offset=*/0, ResultIndex);
ConstAssociationIterator End(Begin.E + NumAssocs, Begin.TSI + NumAssocs,
/*Offset=*/NumAssocs, ResultIndex);
Index: clang/include/clang/AST/ASTNodeTraverser.h
===================================================================
--- clang/include/clang/AST/ASTNodeTraverser.h
+++ clang/include/clang/AST/ASTNodeTraverser.h
@@ -732,8 +732,11 @@
}
void VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
- Visit(E->getControllingExpr());
- Visit(E->getControllingExpr()->getType()); // FIXME: remove
+ if (E->isExprPredicate()) {
+ Visit(E->getControllingExpr());
+ Visit(E->getControllingExpr()->getType()); // FIXME: remove
+ } else
+ Visit(E->getControllingType()->getType());
for (const auto Assoc : E->associations()) {
Visit(Assoc);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits