Skip to content

Commit

Permalink
Issue #26: Fix splices in requires clause (#86)
Browse files Browse the repository at this point in the history
* rename CXXIndeterminateSpliceExpr in the readme too

Signed-off-by: delimbetov <1starfall1@gmail.com>

* make TryAnnotateOptionalCXXScopeToken work

Signed-off-by: delimbetov <1starfall1@gmail.com>

* make splice work in requires clause

Signed-off-by: delimbetov <1starfall1@gmail.com>

* add tests for splice in requires expr

Signed-off-by: delimbetov <1starfall1@gmail.com>

* add typename and newline at the end of the file

Signed-off-by: delimbetov <1starfall1@gmail.com>

* add comments

Signed-off-by: delimbetov <1starfall1@gmail.com>

---------

Signed-off-by: delimbetov <1starfall1@gmail.com>
  • Loading branch information
delimbetov authored Aug 12, 2024
1 parent c8a4568 commit 058106a
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 9 deletions.
3 changes: 2 additions & 1 deletion clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,8 @@ class Parser : public CodeCompletionHandler {
(Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
(Tok.is(tok::annot_template_id) &&
NextToken().is(tok::coloncolon)) ||
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super));
Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super) ||
Tok.isOneOf(tok::l_splice, tok::annot_splice));
}
bool TryAnnotateOptionalCXXScopeToken(bool EnteringContext = false) {
return MightBeCXXScopeToken() && TryAnnotateCXXScopeToken(EnteringContext);
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8554,7 +8554,8 @@ class Sema final : public SemaBase {
CXXScopeSpec &SS,
SourceLocation NameLoc,
const IdentifierInfo *TypeName,
TemplateIdAnnotation *TemplateId);
TemplateIdAnnotation *TemplateId,
CXXSpliceSpecifierExpr *SpliceExpr);
concepts::Requirement *ActOnCompoundRequirement(Expr *E,
SourceLocation NoexceptLoc);
concepts::Requirement *ActOnCompoundRequirement(
Expand Down
13 changes: 10 additions & 3 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3852,25 +3852,32 @@ ExprResult Parser::ParseRequiresExpression() {
ConsumeAnnotationToken();
}

if (Tok.isOneOf(tok::identifier, tok::annot_template_id) &&
if (Tok.isOneOf(
tok::identifier, tok::annot_template_id, tok::annot_splice) &&
!NextToken().isOneOf(tok::l_brace, tok::l_paren)) {
TPA.Commit();
SourceLocation NameLoc = Tok.getLocation();
IdentifierInfo *II = nullptr;
TemplateIdAnnotation *TemplateId = nullptr;
CXXSpliceSpecifierExpr *SpliceExpr = nullptr;
if (Tok.is(tok::identifier)) {
II = Tok.getIdentifierInfo();
ConsumeToken();
} else {
} else if (Tok.is(tok::annot_template_id)) {
TemplateId = takeTemplateIdAnnotation(Tok);
ConsumeAnnotationToken();
if (TemplateId->isInvalid())
break;
} else {
ExprResult Result = getExprAnnotation(Tok);
ConsumeAnnotationToken();
SpliceExpr = dyn_cast<CXXSpliceSpecifierExpr>(Result.get());
}

if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS,
NameLoc, II,
TemplateId)) {
TemplateId,
SpliceExpr)) {
Requirements.push_back(Req);
}
break;
Expand Down
20 changes: 16 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9244,9 +9244,11 @@ concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) {

concepts::Requirement *Sema::ActOnTypeRequirement(
SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc,
const IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId) {
assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) &&
"Exactly one of TypeName and TemplateId must be specified.");
const IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId,
CXXSpliceSpecifierExpr *SpliceExpr) {
assert(((bool(TypeName) + bool(TemplateId) + bool(SpliceExpr)) == 1) &&
"Exactly one of TypeName, TemplateId and SpliceExpr "
"must be specified.");
TypeSourceInfo *TSI = nullptr;
if (TypeName) {
QualType T =
Expand All @@ -9255,7 +9257,7 @@ concepts::Requirement *Sema::ActOnTypeRequirement(
&TSI, /*DeducedTSTContext=*/false);
if (T.isNull())
return nullptr;
} else {
} else if (TemplateId) {
ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS,
Expand All @@ -9268,6 +9270,16 @@ concepts::Requirement *Sema::ActOnTypeRequirement(
return nullptr;
if (GetTypeFromParser(T.get(), &TSI).isNull())
return nullptr;
} else {
TypeResult T = ActOnCXXSpliceExpectingType(
SpliceExpr->getLSpliceLoc(), SpliceExpr->getOperand(), SpliceExpr->getRSpliceLoc(),
true);
if (T.isInvalid()) {
return nullptr;
}
if (GetTypeFromParser(T.get(), &TSI).isNull()) {
return nullptr;
}
}
return BuildTypeRequirement(TSI);
}
Expand Down
159 changes: 159 additions & 0 deletions clang/test/Reflection/splice-types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ constexpr bool is_same_v = false;
template <typename T1>
constexpr bool is_same_v<T1, T1> = true;

template <typename T1, typename T2>
concept same_as = is_same_v<T1, T2>;

template <bool B, class T = void>
struct enable_if {};

template <class T>
struct enable_if<true, T> { typedef T type; };

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

// ===========
// idempotency
// ===========
Expand Down Expand Up @@ -225,3 +237,150 @@ struct T : [:Rs:]... {};
using A = T<^B1, ^B2, ^B3>;
static_assert(A::value1 + A::value2 + A::value3 == 6);
} // namespace base_class_specifiers

// ===============
// requires_clause
// ===============

namespace requires_clause {
struct Addable {
friend Addable operator+(const Addable l, const Addable r) {
return {};
}
};

struct NonAddable {};

namespace Namespace {
using requires_clause::Addable;
using requires_clause::NonAddable;
}

struct HasNested {
struct Nested {};
};

struct NoNested {};

template<typename T, typename = enable_if_t<is_same_v<float, T>>>
struct RequiresFloat {};

template<typename T, typename = enable_if_t<is_same_v<float, T>>>
using AliasFloat = T;


// Simple requirement
template <typename T>
constexpr auto simple_addable = requires(T a, T b) { [:^a:] + b; };
template <typename T>
constexpr auto simple_addable2 = requires(T a, T b) { a + [:^b:]; };
template <typename T>
constexpr auto simple_addable3 = requires(T a, T b) { [:^a:] + [:^b:]; };
template <typename T>
constexpr auto simple_addable_nns =
requires(T b) { [:^Namespace:]::Addable() + b; };
constexpr auto simple_addable_nns2 = requires { [:^Namespace:]::Addable(); };
template <typename T>
constexpr auto simple_dep_nns = requires { typename [:^T:]::Nested(); };

static_assert(simple_addable<Addable>);
static_assert(!simple_addable<NonAddable>);

static_assert(simple_addable2<Addable>);
static_assert(!simple_addable2<NonAddable>);

static_assert(simple_addable3<Addable>);
static_assert(!simple_addable3<NonAddable>);

static_assert(simple_addable_nns<Addable>);
static_assert(!simple_addable_nns<NonAddable>);

static_assert(simple_addable_nns2);

static_assert(simple_dep_nns<HasNested>);
static_assert(!simple_dep_nns<NoNested>);

// Type requirement
template <typename T>
constexpr auto type = requires { typename [:^T:]; };

template <typename T>
constexpr auto type_nested = requires { typename [:^T:]::Nested; };

template <typename T>
constexpr auto type_class_is_float =
requires { typename RequiresFloat<[:^T:]>; };

template <typename T>
constexpr auto type_class_is_float2 =
requires { typename [:^RequiresFloat<T>:]; };

template <typename T>
constexpr auto type_alias_is_float =
requires { typename AliasFloat<[:^T:]>; };

template <typename T>
constexpr auto type_alias_is_float2 =
requires { typename [:^AliasFloat<T>:]; };

static_assert(type<int>);

static_assert(type_nested<HasNested>);
static_assert(!type_nested<NoNested>);

static_assert(type_class_is_float<float>);
static_assert(!type_class_is_float<int>);
static_assert(type_class_is_float2<float>);
static_assert(!type_class_is_float2<int>);

static_assert(type_alias_is_float<float>);
static_assert(!type_alias_is_float<int>);
static_assert(type_alias_is_float2<float>);
static_assert(!type_alias_is_float2<int>);

// Compound requirements
template <typename T>
constexpr auto compound_returns_addable =
requires { {typename [:^T:]()} -> same_as<Addable>; };

template <typename T>
constexpr auto compound_returns_addable2 =
requires { {T()} -> same_as<[:^Addable:]>; };

template <typename T>
constexpr auto compound_returns_addable3 =
requires { {[:^Namespace:]::Addable()} -> same_as<[:^T:]>; };

template <typename T>
constexpr auto compound_returns_addable4 =
requires { {T()} -> same_as<[:^Namespace:]::Addable>; };

static_assert(compound_returns_addable<Addable>);
static_assert(!compound_returns_addable<NonAddable>);

static_assert(compound_returns_addable2<Addable>);
static_assert(!compound_returns_addable2<NonAddable>);

static_assert(compound_returns_addable3<Addable>);
static_assert(!compound_returns_addable3<NonAddable>);

static_assert(compound_returns_addable4<Addable>);
static_assert(!compound_returns_addable4<NonAddable>);

// Nested requirements
template <typename T>
constexpr auto nested_addable = requires {
requires same_as<T, [:^Addable:]>;
};
template <typename T>
constexpr auto nested_addable2 = requires {
requires same_as<T, [:^Namespace:]::Addable>;
};

static_assert(nested_addable<Addable>);
static_assert(!nested_addable<NonAddable>);

static_assert(nested_addable2<Addable>);
static_assert(!nested_addable2<NonAddable>);

} // namespace requires_clause

0 comments on commit 058106a

Please sign in to comment.