Skip to content
Open
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
28 changes: 13 additions & 15 deletions protocol_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -506,41 +506,39 @@ TEST(ProtocolTest, SwapFromValueless) {
EXPECT_TRUE(ppp.valueless_after_move());
}

// A B-shaped type whose methods return covariant/implicitly-convertible
// types instead of B's exact declared return types.
class CovariantBImpl {
std::string last_input_;

public:
// Testing discard: interface returns void, implementation returns int
int process(const std::string& input) {
last_input_ = input;
return 42;
}

// Testing implicit conversion: returns an implicitly convertible struct
// instead of std::vector
struct VectorConvertible {
operator std::vector<int>() const { return {1, 2, 3}; }
};

VectorConvertible get_results() const { return {}; }

// Testing implicit conversion: returns const char* instead of bool
const char* is_ready() const { return "ready"; }

std::string last_input() const { return last_input_; }
};

TEST(ProtocolTest, CovariantReturns) {
xyz::protocol<xyz::B> p(std::in_place_type<CovariantBImpl>);

p.process("hello covariant");
EXPECT_TRUE(p.is_ready());

std::vector<int> results = p.get_results();
ASSERT_EQ(results.size(), 3u);
EXPECT_EQ(results[0], 1);
EXPECT_EQ(results[1], 2);
EXPECT_EQ(results[2], 3);
TEST(ProtocolTest, CovariantReturnsDoNotConform) {
// protocol requires exactly matching function signatures; it does not
// accept conformance through implicit conversion (the relaxed structural
// subtyping DRAFT.md considers and rejects as a design alternative).
// CovariantBImpl's methods are all individually callable but none returns
// B's exact declared type, so it does not conform.
static_assert(
!std::constructible_from<xyz::protocol<xyz::B>,
std::in_place_type_t<CovariantBImpl>>,
"protocol<B> should not be constructible from a type whose methods "
"return covariant/convertible, not exact, types");
}

TEST(ProtocolViewTest, ViewFromMutableConcrete) {
Expand Down
12 changes: 2 additions & 10 deletions scripts/protocol.j2
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ concept protocol_const_concept_{{ c.name }} = requires(const T& t) {
{% set _ = declvals.append("std::declval<" ~ a.type.name ~ ">()") %}
{% endfor %}
{% set args_str = declvals | join(", ") %}
{% if m.return_type.name == 'void' %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %};
{% else %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %} -> std::convertible_to<{{ m.return_type.name }}>;
{% endif %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %} -> std::same_as<{{ m.return_type.name }}>;
{% endif %}
{% endfor %}
};
Expand All @@ -52,11 +48,7 @@ concept protocol_concept_{{ c.name }} = protocol_const_concept_{{ c.name }}<T>{%
{% set _ = declvals.append("std::declval<" ~ a.type.name ~ ">()") %}
{% endfor %}
{% set args_str = declvals | join(", ") %}
{% if m.return_type.name == 'void' %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %};
{% else %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %} -> std::convertible_to<{{ m.return_type.name }}>;
{% endif %}
{ t.{{ m.name }}({{ args_str }}) }{% if m.is_noexcept %} noexcept{% endif %} -> std::same_as<{{ m.return_type.name }}>;
{% endfor %}
}{% endif %};

Expand Down
2 changes: 1 addition & 1 deletion scripts/test_concept_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_wrong_return_type(compile_check: Callable[[str, List[str]], None]) -> N
class BadALike_WrongReturnType {
public:
std::string_view name() const noexcept { return "name"; }
std::string count() { return "42"; } // not convertible to int
std::string count() { return "42"; } // not the same type as int
};
void test() {
Expand Down
Loading