Skip to content

Commit

Permalink
[Clang] Fix parsing of reversible type traits in template arguments (#…
Browse files Browse the repository at this point in the history
…95969)

Constructs like `__is_pointer(Foo)` are never considered to be functions
declarations.

This matches usages in libstdc++, and we can hope
no one else redefine these reserved identifiers.

Fixes #95598
  • Loading branch information
cor3ntin committed Jul 13, 2024
1 parent 40ed194 commit 1fe406f
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 79 deletions.
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,7 @@ Bug Fixes to C++ Support
- Clang now correctly handles unexpanded packs in the template parameter list of a generic lambda expression
(#GH48937)
- Fix a crash when parsing an invalid type-requirement in a requires expression. (#GH51868)
- Fix parsing of built-in type-traits such as ``__is_pointer`` in libstdc++ headers. (#GH95598)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1877,6 +1877,10 @@ class Parser : public CodeCompletionHandler {
UnaryExprOnly,
PrimaryExprOnly
};

bool isRevertibleTypeTrait(const IdentifierInfo *Id,
clang::tok::TokenKind *Kind = nullptr);

ExprResult ParseCastExpression(CastParseKind ParseKind,
bool isAddressOfOperand,
bool &NotCastExpr,
Expand Down
163 changes: 84 additions & 79 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,87 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback {
};
}

bool Parser::isRevertibleTypeTrait(const IdentifierInfo *II,
tok::TokenKind *Kind) {
if (RevertibleTypeTraits.empty()) {
#define RTT_JOIN(X, Y) X##Y
#define REVERTIBLE_TYPE_TRAIT(Name) \
RevertibleTypeTraits[PP.getIdentifierInfo(#Name)] = RTT_JOIN(tok::kw_, Name)

REVERTIBLE_TYPE_TRAIT(__is_abstract);
REVERTIBLE_TYPE_TRAIT(__is_aggregate);
REVERTIBLE_TYPE_TRAIT(__is_arithmetic);
REVERTIBLE_TYPE_TRAIT(__is_array);
REVERTIBLE_TYPE_TRAIT(__is_assignable);
REVERTIBLE_TYPE_TRAIT(__is_base_of);
REVERTIBLE_TYPE_TRAIT(__is_bounded_array);
REVERTIBLE_TYPE_TRAIT(__is_class);
REVERTIBLE_TYPE_TRAIT(__is_complete_type);
REVERTIBLE_TYPE_TRAIT(__is_compound);
REVERTIBLE_TYPE_TRAIT(__is_const);
REVERTIBLE_TYPE_TRAIT(__is_constructible);
REVERTIBLE_TYPE_TRAIT(__is_convertible);
REVERTIBLE_TYPE_TRAIT(__is_convertible_to);
REVERTIBLE_TYPE_TRAIT(__is_destructible);
REVERTIBLE_TYPE_TRAIT(__is_empty);
REVERTIBLE_TYPE_TRAIT(__is_enum);
REVERTIBLE_TYPE_TRAIT(__is_floating_point);
REVERTIBLE_TYPE_TRAIT(__is_final);
REVERTIBLE_TYPE_TRAIT(__is_function);
REVERTIBLE_TYPE_TRAIT(__is_fundamental);
REVERTIBLE_TYPE_TRAIT(__is_integral);
REVERTIBLE_TYPE_TRAIT(__is_interface_class);
REVERTIBLE_TYPE_TRAIT(__is_layout_compatible);
REVERTIBLE_TYPE_TRAIT(__is_literal);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);
REVERTIBLE_TYPE_TRAIT(__is_member_function_pointer);
REVERTIBLE_TYPE_TRAIT(__is_member_object_pointer);
REVERTIBLE_TYPE_TRAIT(__is_member_pointer);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_assignable);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_constructible);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_destructible);
REVERTIBLE_TYPE_TRAIT(__is_nullptr);
REVERTIBLE_TYPE_TRAIT(__is_object);
REVERTIBLE_TYPE_TRAIT(__is_pod);
REVERTIBLE_TYPE_TRAIT(__is_pointer);
REVERTIBLE_TYPE_TRAIT(__is_polymorphic);
REVERTIBLE_TYPE_TRAIT(__is_reference);
REVERTIBLE_TYPE_TRAIT(__is_referenceable);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_reference);
REVERTIBLE_TYPE_TRAIT(__is_same);
REVERTIBLE_TYPE_TRAIT(__is_scalar);
REVERTIBLE_TYPE_TRAIT(__is_scoped_enum);
REVERTIBLE_TYPE_TRAIT(__is_sealed);
REVERTIBLE_TYPE_TRAIT(__is_signed);
REVERTIBLE_TYPE_TRAIT(__is_standard_layout);
REVERTIBLE_TYPE_TRAIT(__is_trivial);
REVERTIBLE_TYPE_TRAIT(__is_trivially_assignable);
REVERTIBLE_TYPE_TRAIT(__is_trivially_constructible);
REVERTIBLE_TYPE_TRAIT(__is_trivially_copyable);
REVERTIBLE_TYPE_TRAIT(__is_unbounded_array);
REVERTIBLE_TYPE_TRAIT(__is_union);
REVERTIBLE_TYPE_TRAIT(__is_unsigned);
REVERTIBLE_TYPE_TRAIT(__is_void);
REVERTIBLE_TYPE_TRAIT(__is_volatile);
REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary);
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait));
#include "clang/Basic/TransformTypeTraits.def"
#undef REVERTIBLE_TYPE_TRAIT
#undef RTT_JOIN
}
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind>::iterator Known =
RevertibleTypeTraits.find(II);
if (Known != RevertibleTypeTraits.end()) {
if (Kind)
*Kind = Known->second;
return true;
}
return false;
}

/// Parse a cast-expression, or, if \pisUnaryExpression is true, parse
/// a unary-expression.
///
Expand Down Expand Up @@ -1118,85 +1199,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
else if (Next.is(tok::l_paren) && Tok.is(tok::identifier) &&
Tok.getIdentifierInfo()->hasRevertedTokenIDToIdentifier()) {
IdentifierInfo *II = Tok.getIdentifierInfo();
// Build up the mapping of revertible type traits, for future use.
if (RevertibleTypeTraits.empty()) {
#define RTT_JOIN(X,Y) X##Y
#define REVERTIBLE_TYPE_TRAIT(Name) \
RevertibleTypeTraits[PP.getIdentifierInfo(#Name)] \
= RTT_JOIN(tok::kw_,Name)

REVERTIBLE_TYPE_TRAIT(__is_abstract);
REVERTIBLE_TYPE_TRAIT(__is_aggregate);
REVERTIBLE_TYPE_TRAIT(__is_arithmetic);
REVERTIBLE_TYPE_TRAIT(__is_array);
REVERTIBLE_TYPE_TRAIT(__is_assignable);
REVERTIBLE_TYPE_TRAIT(__is_base_of);
REVERTIBLE_TYPE_TRAIT(__is_bounded_array);
REVERTIBLE_TYPE_TRAIT(__is_class);
REVERTIBLE_TYPE_TRAIT(__is_complete_type);
REVERTIBLE_TYPE_TRAIT(__is_compound);
REVERTIBLE_TYPE_TRAIT(__is_const);
REVERTIBLE_TYPE_TRAIT(__is_constructible);
REVERTIBLE_TYPE_TRAIT(__is_convertible);
REVERTIBLE_TYPE_TRAIT(__is_convertible_to);
REVERTIBLE_TYPE_TRAIT(__is_destructible);
REVERTIBLE_TYPE_TRAIT(__is_empty);
REVERTIBLE_TYPE_TRAIT(__is_enum);
REVERTIBLE_TYPE_TRAIT(__is_floating_point);
REVERTIBLE_TYPE_TRAIT(__is_final);
REVERTIBLE_TYPE_TRAIT(__is_function);
REVERTIBLE_TYPE_TRAIT(__is_fundamental);
REVERTIBLE_TYPE_TRAIT(__is_integral);
REVERTIBLE_TYPE_TRAIT(__is_interface_class);
REVERTIBLE_TYPE_TRAIT(__is_layout_compatible);
REVERTIBLE_TYPE_TRAIT(__is_literal);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);
REVERTIBLE_TYPE_TRAIT(__is_member_function_pointer);
REVERTIBLE_TYPE_TRAIT(__is_member_object_pointer);
REVERTIBLE_TYPE_TRAIT(__is_member_pointer);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_assignable);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_constructible);
REVERTIBLE_TYPE_TRAIT(__is_nothrow_destructible);
REVERTIBLE_TYPE_TRAIT(__is_nullptr);
REVERTIBLE_TYPE_TRAIT(__is_object);
REVERTIBLE_TYPE_TRAIT(__is_pod);
REVERTIBLE_TYPE_TRAIT(__is_pointer);
REVERTIBLE_TYPE_TRAIT(__is_polymorphic);
REVERTIBLE_TYPE_TRAIT(__is_reference);
REVERTIBLE_TYPE_TRAIT(__is_referenceable);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_rvalue_reference);
REVERTIBLE_TYPE_TRAIT(__is_same);
REVERTIBLE_TYPE_TRAIT(__is_scalar);
REVERTIBLE_TYPE_TRAIT(__is_scoped_enum);
REVERTIBLE_TYPE_TRAIT(__is_sealed);
REVERTIBLE_TYPE_TRAIT(__is_signed);
REVERTIBLE_TYPE_TRAIT(__is_standard_layout);
REVERTIBLE_TYPE_TRAIT(__is_trivial);
REVERTIBLE_TYPE_TRAIT(__is_trivially_assignable);
REVERTIBLE_TYPE_TRAIT(__is_trivially_constructible);
REVERTIBLE_TYPE_TRAIT(__is_trivially_copyable);
REVERTIBLE_TYPE_TRAIT(__is_unbounded_array);
REVERTIBLE_TYPE_TRAIT(__is_union);
REVERTIBLE_TYPE_TRAIT(__is_unsigned);
REVERTIBLE_TYPE_TRAIT(__is_void);
REVERTIBLE_TYPE_TRAIT(__is_volatile);
REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary);
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait));
#include "clang/Basic/TransformTypeTraits.def"
#undef REVERTIBLE_TYPE_TRAIT
#undef RTT_JOIN
}

// If we find that this is in fact the name of a type trait,
// update the token kind in place and parse again to treat it as
// the appropriate kind of type trait.
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind>::iterator Known
= RevertibleTypeTraits.find(II);
if (Known != RevertibleTypeTraits.end()) {
Tok.setKind(Known->second);
tok::TokenKind Kind;
if (isRevertibleTypeTrait(II, &Kind)) {
Tok.setKind(Kind);
return ParseCastExpression(ParseKind, isAddressOfOperand,
NotCastExpr, isTypeCast,
isVectorLiteral, NotPrimaryExpression);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,15 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
if (!getLangOpts().ObjC && Next.is(tok::identifier))
return TPResult::True;

// If this identifier was reverted from a token ID, and the next token
// is a '(', we assume it to be a use of a type trait, so this
// can never be a type name.
if (Next.is(tok::l_paren) &&
Tok.getIdentifierInfo()->hasRevertedTokenIDToIdentifier() &&
isRevertibleTypeTrait(Tok.getIdentifierInfo())) {
return TPResult::False;
}

if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less)) {
// Determine whether this is a valid expression. If not, we will hit
// a parse error one way or another. In that case, tell the caller that
Expand Down
12 changes: 12 additions & 0 deletions clang/test/Parser/cxx-template-argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,15 @@ namespace r360308_regression {
return a == b;
}
}

namespace GH95598 {
template<typename _Tp, bool _IsPtr = __is_pointer(_Tp)>
struct __is_pointer {};
// expected-warning@-1 {{keyword '__is_pointer' will be made available as an identifier for the remainder of the translation unit}}

template<bool>
struct ts{};

template<typename _Tp>
struct is_pointer : ts<__is_pointer(_Tp)> {};
}

0 comments on commit 1fe406f

Please sign in to comment.