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
75 changes: 71 additions & 4 deletions common/decl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <cstddef>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -102,13 +103,79 @@ bool SignaturesOverlap(const OverloadDecl& lhs, const OverloadDecl& rhs) {
return args_overlap;
}

// Recursively appends a string representation of the given `type` to `result`.
// Type parameters are enclosed in angle brackets and separated by commas.
void AppendTypeToOverloadId(std::string* result, const Type& type) {
const auto& parameters = type.GetParameters();
if (parameters.empty()) {
absl::StrAppend(result, type.name());
} else {
absl::StrAppend(result, type.name(), "<");
for (size_t i = 0; i < parameters.size(); ++i) {
AppendTypeToOverloadId(result, parameters[i]);
if (i < parameters.size() - 1) {
absl::StrAppend(result, ",");
}
}
absl::StrAppend(result, ">");
}
}

// Generates an identifier for the overload based on the function name and
// the types of the arguments. If `member` is true, the first argument type
// is used as the receiver and is prepended to the function name, followed by
// a dot.
//
// Examples:
//
// - `foo()`
// - `foo(int)`
// - `bar.foo(int)`
// - `foo(int,string)`
// - `foo(list<int>,list<string>)`
// - `bar.foo(list<int>, list<my_type<A>>)`
//
std::string GenerateOverloadId(std::string_view function_name,
const std::vector<Type>& args, bool member) {
std::string result;
result.reserve(function_name.size() + 3 + args.size() * 8);

if (member) {
if (!args.empty()) {
AppendTypeToOverloadId(&result, args[0]);
}
absl::StrAppend(&result, ".");
}
absl::StrAppend(&result, function_name, "(");
for (size_t i = member ? 1 : 0; i < args.size(); ++i) {
AppendTypeToOverloadId(&result, args[i]);
if (i < args.size() - 1) {
absl::StrAppend(&result, ",");
}
}
absl::StrAppend(&result, ")");

return result;
}

template <typename Overload>
void AddOverloadInternal(std::vector<OverloadDecl>& insertion_order,
void AddOverloadInternal(std::string_view function_name,
std::vector<OverloadDecl>& insertion_order,
OverloadDeclHashSet& overloads, Overload&& overload,
absl::Status& status) {
if (!status.ok()) {
return;
}

if (overload.id().empty()) {
OverloadDecl overload_decl = overload;
overload_decl.set_id(GenerateOverloadId(function_name, overload_decl.args(),
overload_decl.member()));
AddOverloadInternal(function_name, insertion_order, overloads,
std::move(overload_decl), status);
return;
}

if (auto it = overloads.find(overload.id()); it != overloads.end()) {
status = absl::AlreadyExistsError(
absl::StrCat("overload already exists: ", overload.id()));
Expand Down Expand Up @@ -174,13 +241,13 @@ absl::flat_hash_set<std::string> OverloadDecl::GetTypeParams() const {

void FunctionDecl::AddOverloadImpl(const OverloadDecl& overload,
absl::Status& status) {
AddOverloadInternal(overloads_.insertion_order, overloads_.set, overload,
status);
AddOverloadInternal(name_, overloads_.insertion_order, overloads_.set,
overload, status);
}

void FunctionDecl::AddOverloadImpl(OverloadDecl&& overload,
absl::Status& status) {
AddOverloadInternal(overloads_.insertion_order, overloads_.set,
AddOverloadInternal(name_, overloads_.insertion_order, overloads_.set,
std::move(overload), status);
}

Expand Down
28 changes: 28 additions & 0 deletions common/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ inline bool operator!=(const OverloadDecl& lhs, const OverloadDecl& rhs) {
return !operator==(lhs, rhs);
}

template <typename... Args>
OverloadDecl MakeOverloadDecl(Type result, Args&&... args) {
OverloadDecl overload_decl;
overload_decl.set_result(std::move(result));
overload_decl.set_member(false);
auto& mutable_args = overload_decl.mutable_args();
mutable_args.reserve(sizeof...(Args));
(mutable_args.push_back(std::forward<Args>(args)), ...);
return overload_decl;
}

// Avoid this version of `MakeOverloadDecl`, it is less robust than the
// version that automatically generates a descriptive overload id at the time
// the overload is added to the function declaration.
template <typename... Args>
OverloadDecl MakeOverloadDecl(absl::string_view id, Type result,
Args&&... args) {
Expand All @@ -222,6 +236,20 @@ OverloadDecl MakeOverloadDecl(absl::string_view id, Type result,
return overload_decl;
}

template <typename... Args>
OverloadDecl MakeMemberOverloadDecl(Type result, Args&&... args) {
OverloadDecl overload_decl;
overload_decl.set_result(std::move(result));
overload_decl.set_member(true);
auto& mutable_args = overload_decl.mutable_args();
mutable_args.reserve(sizeof...(Args));
(mutable_args.push_back(std::forward<Args>(args)), ...);
return overload_decl;
}

// Avoid this version of `MakeMemberOverloadDecl`, it is less robust than the
// version that automatically generates a descriptive overload id at the time
// the overload is added to the function declaration.
template <typename... Args>
OverloadDecl MakeMemberOverloadDecl(absl::string_view id, Type result,
Args&&... args) {
Expand Down
33 changes: 33 additions & 0 deletions common/decl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,39 @@ TEST(FunctionDecl, Overloads) {
StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST(FunctionDecl, OverloadId) {
google::protobuf::Arena arena;
ASSERT_OK_AND_ASSIGN(
auto function_decl,
MakeFunctionDecl(
"hello", MakeOverloadDecl(DoubleType{}),
MakeOverloadDecl(StringType{}, StringType{}),
MakeOverloadDecl(IntType{}, IntType{}, UintType{}),
MakeOverloadDecl(
ListType(&arena, TypeParamType("A")),
MapType(&arena, TypeParamType("B"), TypeParamType("C")),
OpaqueType(&arena, "bar",
{FunctionType(&arena, TypeParamType("D"), {})})),
MakeMemberOverloadDecl(IntType{}),
MakeMemberOverloadDecl(StringType{}, StringType{}),
MakeMemberOverloadDecl(StringType{}, StringType{},
ListType(&arena, BoolType{})),
MakeMemberOverloadDecl(StringType{}, StringType{}, BoolType{},
DynType{})));

EXPECT_THAT(
function_decl.overloads(),
ElementsAre(
Property(&OverloadDecl::id, "hello()"),
Property(&OverloadDecl::id, "hello(string)"),
Property(&OverloadDecl::id, "hello(int,uint)"),
Property(&OverloadDecl::id, "hello(map<B,C>,bar<function<D>>)"),
Property(&OverloadDecl::id, ".hello()"),
Property(&OverloadDecl::id, "string.hello()"),
Property(&OverloadDecl::id, "string.hello(list<bool>)"),
Property(&OverloadDecl::id, "string.hello(bool,dyn)")));
}

using common_internal::TypeIsAssignable;

TEST(TypeIsAssignable, BoolWrapper) {
Expand Down