Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -1349,6 +1349,17 @@ appropriate standard, but in C++, which lacks the type compatibility rules used in C, types are considered compatible only if they are equivalent. +Clang also supports an extended form of ``_Generic`` with a controlling type +rather than a controlling expression. Unlike with a controlling expression, a +controlling type argument does not undergo any conversions and thus is suitable +for use when trying to match qualified types, incomplete types, or function +types. Variable-length array types lack the necessary compile-time information +to resolve which association they match with and thus are not allowed as a +controlling type argument. + +Use ``__has_extension(c_generic_selection_with_controlling_type)`` to determine +if support for this extension is enabled. + C11 ``_Static_assert()`` ^^^^^^^^^^^^^^^^^^^^^^^^ Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -114,6 +114,17 @@ from a null pointer constant. - Fixed a bug that prevented casting to an ``_Atomic``-qualified type. (`#39596 `_) +- Added an extension to ``_Generic`` which allows the first operand to be a + type rather than an expression. The type does not undergo any conversions, + which makes this feature suitable for matching qualified types, incomplete + types, and function or array types. + + .. code-block:: c + + const int i = 12; + _Generic(i, int : 0, const int : 1); // Warns about unreachable code, the + // result is 0, not 1. + _Generic(typeof(i), int : 0, const int : 1); // Result is 1, not 0. C2x Feature Support ^^^^^^^^^^^^^^^^^^^ 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); 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::max(), - ControllingIndex = 0, - AssocExprStartIndex = 1 + ResultDependentIndex = 0x7FFF }; + 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) 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) const { - return getNumAssocs(); + // Add one to account for the controlling type predicate, the remainder + // are the associated types. + return getNumAssocs() + (int)isTypePredicate(); } template class AssociationIteratorTy; @@ -5782,7 +5824,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 AssocTypes, @@ -5791,7 +5834,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 AssocTypes, @@ -5799,11 +5843,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 AssocTypes, + ArrayRef 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 AssocTypes, + ArrayRef 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 AssocTypes, @@ -5811,13 +5875,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 AssocTypes, ArrayRef 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 AssocTypes, + ArrayRef 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 AssocTypes, + ArrayRef 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 +5927,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(getTrailingObjects()[ControllingIndex]); + return cast( + getTrailingObjects()[getIndexOfControllingExpression()]); } const Expr *getControllingExpr() const { - return cast(getTrailingObjects()[ControllingIndex]); + return cast( + getTrailingObjects()[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()[getIndexOfControllingType()]; + } + const TypeSourceInfo* getControllingType() const { + return getTrailingObjects()[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( - getTrailingObjects()[AssocExprStartIndex + getResultIndex()]); + getTrailingObjects()[getIndexOfStartOfAssociatedExprs() + + getResultIndex()]); } const Expr *getResultExpr() const { return cast( - getTrailingObjects()[AssocExprStartIndex + getResultIndex()]); + getTrailingObjects()[getIndexOfStartOfAssociatedExprs() + + getResultIndex()]); } ArrayRef getAssocExprs() const { return {reinterpret_cast(getTrailingObjects() + - AssocExprStartIndex), + getIndexOfStartOfAssociatedExprs()), NumAssocs}; } ArrayRef getAssocTypeSourceInfos() const { - return {getTrailingObjects(), NumAssocs}; + return {getTrailingObjects() + + getIndexOfStartOfAssociatedTypes(), + NumAssocs}; } /// Return the Ith association expression with its TypeSourceInfo, @@ -5879,23 +5985,30 @@ assert(I < getNumAssocs() && "Out-of-range index in GenericSelectionExpr::getAssociation!"); return Association( - cast(getTrailingObjects()[AssocExprStartIndex + I]), - getTrailingObjects()[I], + cast( + getTrailingObjects()[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(getTrailingObjects()[AssocExprStartIndex + I]), - getTrailingObjects()[I], + cast( + getTrailingObjects()[getIndexOfStartOfAssociatedExprs() + + I]), + getTrailingObjects< + TypeSourceInfo *>()[getIndexOfStartOfAssociatedTypes() + I], !isResultDependent() && (getResultIndex() == I)); } association_range associations() { AssociationIterator Begin(getTrailingObjects() + - AssocExprStartIndex, - getTrailingObjects(), + getIndexOfStartOfAssociatedExprs(), + getTrailingObjects() + + getIndexOfStartOfAssociatedTypes(), /*Offset=*/0, ResultIndex); AssociationIterator End(Begin.E + NumAssocs, Begin.TSI + NumAssocs, /*Offset=*/NumAssocs, ResultIndex); @@ -5904,8 +6017,9 @@ const_association_range associations() const { ConstAssociationIterator Begin(getTrailingObjects() + - AssocExprStartIndex, - getTrailingObjects(), + getIndexOfStartOfAssociatedExprs(), + getTrailingObjects() + + getIndexOfStartOfAssociatedTypes(), /*Offset=*/0, ResultIndex); ConstAssociationIterator End(Begin.E + NumAssocs, Begin.TSI + NumAssocs, /*Offset=*/NumAssocs, ResultIndex); 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/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>; def ext_c99_feature : Extension< "'%0' is a C99 extension">, InGroup; Index: clang/include/clang/Basic/Features.def =================================================================== --- clang/include/clang/Basic/Features.def +++ clang/include/clang/Basic/Features.def @@ -242,6 +242,7 @@ EXTENSION(c_alignof, true) EXTENSION(c_atomic, true) EXTENSION(c_generic_selections, true) +EXTENSION(c_generic_selection_with_controlling_type, true) EXTENSION(c_static_assert, true) EXTENSION(c_thread_local, PP.getTargetInfo().isTLSSupported()) // C++11 features supported by other languages as extensions. 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,13 +2540,29 @@ 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() { + if (getLangOpts().CPlusPlus) { + bool isAmbiguous; + 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. bool isTypeIdUnambiguously() { - bool IsAmbiguous; - if (getLangOpts().CPlusPlus) - return isCXXTypeId(TypeIdUnambiguous, IsAmbiguous); + if (getLangOpts().CPlusPlus) { + bool isAmbiguous; + return isCXXTypeId(TypeIdUnambiguous, isAmbiguous); + } return isTypeSpecifierQualifier(); } Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -5688,16 +5688,27 @@ ExprResult ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope = nullptr); + /// ControllingExprOrType is either an opaque pointer coming out of a + /// ParsedType or an Expr *. FIXME: it'd be better to split this interface + /// into two so we don't take a void *, but that's awkward because one of + /// the operands is either a ParsedType or an Expr *, which doesn't lend + /// itself to generic code very well. ExprResult ActOnGenericSelectionExpr(SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc, - Expr *ControllingExpr, + bool PredicateIsExpr, + void *ControllingExprOrType, ArrayRef ArgTypes, ArrayRef ArgExprs); + /// ControllingExprOrType is either a TypeSourceInfo * or an Expr *. FIXME: + /// it'd be better to split this interface into two so we don't take a + /// void *, but see the FIXME on ActOnGenericSelectionExpr as to why that + /// isn't a trivial change. ExprResult CreateGenericSelectionExpr(SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc, - Expr *ControllingExpr, + bool PredicateIsExpr, + void *ControllingExprOrType, ArrayRef Types, ArrayRef Exprs); 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/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/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()[ControllingIndex] = ControllingExpr; + getTrailingObjects()[getIndexOfControllingExpression()] = + ControllingExpr; std::copy(AssocExprs.begin(), AssocExprs.end(), - getTrailingObjects() + AssocExprStartIndex); + getTrailingObjects() + getIndexOfStartOfAssociatedExprs()); std::copy(AssocTypes.begin(), AssocTypes.end(), - getTrailingObjects()); + getTrailingObjects() + + getIndexOfStartOfAssociatedTypes()); + + setDependence(computeDependence(this, ContainsUnexpandedParameterPack)); +} + +GenericSelectionExpr::GenericSelectionExpr( + const ASTContext &, SourceLocation GenericLoc, + TypeSourceInfo *ControllingType, ArrayRef AssocTypes, + ArrayRef 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()[getIndexOfControllingType()] = + ControllingType; + std::copy(AssocExprs.begin(), AssocExprs.end(), + getTrailingObjects() + getIndexOfStartOfAssociatedExprs()); + std::copy(AssocTypes.begin(), AssocTypes.end(), + getTrailingObjects() + + 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()[getIndexOfControllingExpression()] = + ControllingExpr; + std::copy(AssocExprs.begin(), AssocExprs.end(), + getTrailingObjects() + getIndexOfStartOfAssociatedExprs()); + std::copy(AssocTypes.begin(), AssocTypes.end(), + getTrailingObjects() + + getIndexOfStartOfAssociatedTypes()); + + setDependence(computeDependence(this, ContainsUnexpandedParameterPack)); +} + +GenericSelectionExpr::GenericSelectionExpr( + const ASTContext &Context, SourceLocation GenericLoc, + TypeSourceInfo *ControllingType, ArrayRef AssocTypes, + ArrayRef 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()[ControllingIndex] = ControllingExpr; + getTrailingObjects()[getIndexOfControllingType()] = + ControllingType; std::copy(AssocExprs.begin(), AssocExprs.end(), - getTrailingObjects() + AssocExprStartIndex); + getTrailingObjects() + getIndexOfStartOfAssociatedExprs()); std::copy(AssocTypes.begin(), AssocTypes.end(), - getTrailingObjects()); + getTrailingObjects() + + getIndexOfStartOfAssociatedTypes()); setDependence(computeDependence(this, ContainsUnexpandedParameterPack)); } @@ -4390,6 +4447,35 @@ RParenLoc, ContainsUnexpandedParameterPack); } +GenericSelectionExpr *GenericSelectionExpr::Create( + const ASTContext &Context, SourceLocation GenericLoc, + TypeSourceInfo *ControllingType, ArrayRef AssocTypes, + ArrayRef AssocExprs, SourceLocation DefaultLoc, + SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack, + unsigned ResultIndex) { + unsigned NumAssocs = AssocExprs.size(); + void *Mem = Context.Allocate( + totalSizeToAlloc(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 AssocTypes, + ArrayRef AssocExprs, SourceLocation DefaultLoc, + SourceLocation RParenLoc, bool ContainsUnexpandedParameterPack) { + unsigned NumAssocs = AssocExprs.size(); + void *Mem = Context.Allocate( + totalSizeToAlloc(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/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/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, InnerMatcher) { + if (Node.isTypePredicate()) + return false; return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); } 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,20 @@ 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(); + } + const auto *LIT = cast(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 +3374,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/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 supposed 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/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 ArgTypes, - ArrayRef ArgExprs) { +ExprResult Sema::ActOnGenericSelectionExpr( + SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc, + bool PredicateIsExpr, void *ControllingExprOrType, + ArrayRef ArgTypes, ArrayRef 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 Types, - ArrayRef Exprs) { +ExprResult Sema::CreateGenericSelectionExpr( + SourceLocation KeyLoc, SourceLocation DefaultLoc, SourceLocation RParenLoc, + bool PredicateIsExpr, void *ControllingExprOrType, + ArrayRef Types, ArrayRef 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(ControllingExprOrType)); if (R.isInvalid()) return ExprError(); ControllingExpr = R.get(); + } else { + // The extension form uses the type directly rather than converting it. + ControllingType = reinterpret_cast(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 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/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -4551,6 +4551,7 @@ CurFPFeatureOverrides()); } else if (GenericSelectionExpr *gse = dyn_cast(e)) { assert(!gse->isResultDependent()); + assert(!gse->isTypePredicate()); unsigned n = gse->getNumAssocs(); SmallVector subExprs; 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/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/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 Types, ArrayRef 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 Types, + ArrayRef 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 ExprResult TreeTransform::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 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 Index: clang/lib/Serialization/ASTReaderStmt.cpp =================================================================== --- clang/lib/Serialization/ASTReaderStmt.cpp +++ clang/lib/Serialization/ASTReaderStmt.cpp @@ -1357,6 +1357,7 @@ unsigned NumAssocs = Record.readInt(); assert(NumAssocs == E->getNumAssocs() && "Wrong NumAssocs!"); + E->IsExprPredicate = Record.readInt(); E->ResultIndex = Record.readInt(); E->GenericSelectionExprBits.GenericLoc = readSourceLocation(); E->DefaultLoc = readSourceLocation(); Index: clang/lib/Serialization/ASTWriterStmt.cpp =================================================================== --- clang/lib/Serialization/ASTWriterStmt.cpp +++ clang/lib/Serialization/ASTWriterStmt.cpp @@ -1224,6 +1224,7 @@ VisitExpr(E); Record.push_back(E->getNumAssocs()); + Record.push_back(E->isExprPredicate()); Record.push_back(E->ResultIndex); Record.AddSourceLocation(E->getGenericLoc()); Record.AddSourceLocation(E->getDefaultLoc()); Index: clang/test/Lexer/has_extension.c =================================================================== --- clang/test/Lexer/has_extension.c +++ clang/test/Lexer/has_extension.c @@ -20,6 +20,14 @@ int no_c_static_assert(); #endif +// CHECK-PED-NONE: has_c_generic_selections_with_controlling_type +// CHECK-PED-ERR: no_c_generic_selections_with_controlling_type +#if __has_extension(c_generic_selection_with_controlling_type) +int has_c_generic_selections_with_controlling_type(); +#else +int no_c_generic_selections_with_controlling_type(); +#endif + // CHECK-PED-NONE: has_c_generic_selections // CHECK-PED-ERR: no_c_generic_selections #if __has_extension(c_generic_selections) 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/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 +struct S { + template