Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #26: Fix splices in requires clause #85

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
155 changes: 155 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,146 @@ struct T : [:Rs:]... {};
using A = T<^B1, ^B2, ^B3>;
static_assert(A::value1 + A::value2 + A::value3 == 6);
} // namespace base_class_specifiers

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>);

}
Loading