Skip to content

Commit 7089ab6

Browse files
committed
a bit more progress on generics maybe
1 parent 9389e8e commit 7089ab6

File tree

13 files changed

+172
-24
lines changed

13 files changed

+172
-24
lines changed

Makefile

+2-7
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ else ifeq ("$(shell echo '$(CXX_MAJOR_VERSION) >= 19' | bc)", "1")
2424
WARNINGS += -Wno-missing-designated-field-initializers
2525
endif
2626

27-
OPT_FLAGS := -O0 -fsanitize=address
27+
OPT_FLAGS := -O0 -fsanitize=address -g
2828
LINKER_OPT_FLAGS :=
29-
COMMON_CFLAGS := $(OPT_FLAGS)
29+
COMMON_CFLAGS := $(OPT_FLAGS) -fvisibility=hidden
3030

3131
OUTPUT_DIR := build
3232
TEST_DIR := $(OUTPUT_DIR)/test
@@ -137,17 +137,12 @@ $(OUTPUT_BIN): $(PRECOMP_OBJ) $(CXXOBJ) $(EXTERNAL_OBJS)
137137
@echo " $(notdir $@)"
138138
@mkdir -p $(shell dirname $@)
139139
@$(CXX) $(CXXFLAGS) $(WARNINGS) $(DEFINES) $(LDFLAGS) $(LINKER_OPT_FLAGS) -Iexternal -o $@ $^
140-
@case "$(CXXFLAGS)" in "-g "* | *" -g" | *" -g "*) \
141-
echo " debuginfo"; dsymutil $@ 2>/dev/null & \
142-
disown -a ;; *) ;; esac
143140

144141

145142
$(TESTER_BIN): $(PRECOMP_OBJ) $(EXTERNAL_OBJS) $(filter-out $(OUTPUT_DIR)/source/main.cpp.o,$(CXXOBJ)) $(TESTOBJ)
146143
@echo " $(notdir $@)"
147144
@mkdir -p $(shell dirname $@)
148145
@$(CXX) $(CXXFLAGS) $(WARNINGS) $(DEFINES) $(LDFLAGS) $(LINKER_OPT_FLAGS) -Iexternal -o $@ $^
149-
@# @case "$(CXXFLAGS)" in "-g "* | *" -g" | *" -g "*) \
150-
@# echo " debuginfo"; dsymutil $@ 2>/dev/null ;; *) ;; esac
151146

152147

153148

source/defs.h

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#pragma once
66

77
#include <string>
8+
#include <filesystem>
89

910
#include <zpr.h>
1011
#include <zst/zst.h>
@@ -36,6 +37,8 @@ namespace sap
3637
return Ok(std::move(x));
3738
}
3839

40+
std::filesystem::path getInvocationCWD();
41+
3942
namespace watch
4043
{
4144
bool isWatching();

source/interp/ast/call.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ namespace sap::interp::ast
336336

337337
auto ufcs_kind = UFCSKind::None;
338338
auto final_callee = TRY(this->callee->typecheck(ts)).take_expr();
339+
340+
if(const auto* pso = dynamic_cast<const cst::PartiallyResolvedOverloadSet*>(final_callee.get()))
341+
{
342+
return ErrMsg(this->loc(), "a");
343+
}
344+
339345
if(not final_callee->type()->isFunction())
340346
{
341347
return ErrMsg(ts, "callee of function call must be a function type, got '{}'",

source/interp/ast/subscript_op.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ namespace sap::interp::ast
3333
goto not_generic_function;
3434

3535
// ok we have at least one generic function, try to do some generic stuff.
36-
return polymorph::tryInstantiateGenericFunction(ts, infer, std::move(decls), this->indices);
36+
return polymorph::tryInstantiateGenericFunction(ts, infer, lhs_ident->name, std::move(decls),
37+
this->indices);
3738
}
3839

3940
not_generic_function:

source/interp/cst.h

+22
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,28 @@ namespace sap::interp::cst
953953
std::vector<ExprOrDefaultPtr> values;
954954
};
955955

956+
struct PartiallyResolvedOverloadSet : Expr
957+
{
958+
struct Item
959+
{
960+
const cst::Declaration* decl;
961+
util::hashmap<std::string, const Type*> applied_types;
962+
util::hashmap<std::string, std::unique_ptr<cst::Expr>> applied_args;
963+
964+
ErrorMessage exclusion_reason;
965+
};
966+
967+
explicit PartiallyResolvedOverloadSet(Location loc, QualifiedId name)
968+
: Expr(std::move(loc), Type::makeVoid()), name(std::move(name))
969+
{
970+
}
971+
972+
virtual ErrorOr<EvalResult> evaluate_impl(Evaluator* ev) const override;
973+
974+
QualifiedId name;
975+
std::vector<Item> items;
976+
std::vector<Item> excluded_items;
977+
};
956978

957979
struct IfLetUnionStmt : Stmt
958980
{

source/interp/cst/func.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,16 @@ namespace sap::interp::cst
3434
{
3535
return EvalResult::ofVoid();
3636
}
37+
38+
ErrorOr<EvalResult> PartiallyResolvedOverloadSet::evaluate_impl(Evaluator* ev) const
39+
{
40+
if(this->items.empty())
41+
return ErrMsg(this->loc(), "Reference to overload set '{}' resulted in an empty set", this->name);
42+
43+
auto e = ErrorMessage(this->loc(), zpr::sprint("Ambiguous reference to '{}' in overload set", this->name));
44+
for(auto& item : this->items)
45+
e.addInfo(item.decl->location, zpr::sprint("Possible candidate:"));
46+
47+
return Err(std::move(e));
48+
}
3749
}

source/interp/defn_tree.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,6 @@ namespace sap::interp
255255

256256
ErrorOr<cst::Declaration*> DefnTree::declare(cst::Declaration new_decl)
257257
{
258-
zpr::println("making new {}: type={}, gf={}", new_decl.name, new_decl.type->str(),
259-
new_decl.generic_func != nullptr);
260-
261258
auto check_existing = [this, &new_decl](const auto& existing_decls) -> ErrorOr<void> {
262259
for(auto& decl_ : existing_decls)
263260
{
@@ -272,13 +269,6 @@ namespace sap::interp
272269
if(decl == new_decl)
273270
return Ok();
274271

275-
276-
// if (at least) one of them is a generic function and the other is a normal function (or a generic
277-
// func) then we pass; we can't really disambiguate them at this point without instantiating the
278-
// template.
279-
zpr::println("old: {}, gf: {}", decl.name, decl.generic_func != nullptr);
280-
zpr::println("new: {}, gf: {}", new_decl.name, new_decl.generic_func != nullptr);
281-
282272
if(new_decl.generic_func != nullptr && (decl.generic_func != nullptr || decl.type->isFunction()))
283273
{
284274
// ok

source/interp/polymorph.cpp

+109-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,122 @@
66
#include "interp/type.h"
77
#include "interp/interp.h"
88
#include "interp/polymorph.h"
9+
#include "interp/overload_resolution.h"
910

1011
namespace sap::interp::polymorph
1112
{
13+
using PSOItem = cst::PartiallyResolvedOverloadSet::Item;
14+
15+
static std::pair<PSOItem, bool> match_generic_arguments(Typechecker* ts,
16+
const Type* infer,
17+
const cst::Declaration* decl,
18+
const ast::FunctionDefn* fn,
19+
const std::vector<ast::FunctionCall::Arg>& explicit_args)
20+
{
21+
bool failed = false;
22+
PSOItem item { .decl = decl };
23+
24+
#define _fail_with(loc, msg, ...) \
25+
__extension__({ \
26+
failed = true; \
27+
item.exclusion_reason.addInfo((loc), zpr::sprint((msg) __VA_OPT__(, ) __VA_ARGS__)); \
28+
continue; \
29+
})
30+
31+
util::hashset<size_t> seen_param_indices {};
32+
util::hashmap<std::string, size_t> expected_arg_names {};
33+
for(size_t i = 0; i < fn->generic_params.size(); i++)
34+
expected_arg_names[fn->generic_params[i].name] = i;
35+
36+
bool saw_named_arg = false;
37+
size_t positional_param_idx = 0;
38+
for(auto& arg : explicit_args)
39+
{
40+
if(arg.name.has_value())
41+
{
42+
saw_named_arg = true;
43+
44+
const auto it = expected_arg_names.find(*arg.name);
45+
if(it == expected_arg_names.end())
46+
_fail_with(arg.value->loc(), "Entity has no parameter named '{}'", *arg.name);
47+
48+
const size_t param_idx = it->second;
49+
if(seen_param_indices.contains(param_idx))
50+
_fail_with(arg.value->loc(), "Duplicate argument for paramter '{}'", *arg.name);
51+
52+
seen_param_indices.insert(param_idx);
53+
54+
auto te = arg.value->typecheck(ts);
55+
if(te.is_err())
56+
_fail_with(arg.value->loc(), "{}", te.error().string());
57+
58+
item.applied_types[*arg.name] = te->type();
59+
}
60+
else
61+
{
62+
if(saw_named_arg)
63+
_fail_with(arg.value->loc(), "Positional arguments are not allowed after named ones");
64+
65+
if(positional_param_idx > expected_arg_names.size())
66+
{
67+
_fail_with(arg.value->loc(), "too many argumentss provided; expected at most {}",
68+
expected_arg_names.size());
69+
}
70+
71+
const auto param_idx = positional_param_idx++;
72+
seen_param_indices.insert(param_idx);
73+
74+
auto te = arg.value->typecheck(ts);
75+
if(te.is_err())
76+
_fail_with(arg.value->loc(), "{}", te.error().string());
77+
78+
item.applied_types[fn->generic_params[param_idx].name] = te->type();
79+
}
80+
}
81+
82+
return { std::move(item), not failed };
83+
84+
#undef _fail_with
85+
}
86+
87+
1288
ErrorOr<TCResult> tryInstantiateGenericFunction(Typechecker* ts,
1389
const Type* infer,
90+
const QualifiedId& name,
1491
std::vector<const cst::Declaration*> declarations,
1592
const std::vector<ast::FunctionCall::Arg>& explicit_args)
1693
{
17-
return ErrMsg(ts, "hello");
94+
auto set = std::make_unique<cst::PartiallyResolvedOverloadSet>(ts->loc(), name);
95+
96+
for(const auto* decl : declarations)
97+
{
98+
if(decl->generic_func)
99+
{
100+
auto [item, ok] = match_generic_arguments(ts, infer, decl, decl->generic_func, explicit_args);
101+
if(ok)
102+
set->items.push_back(std::move(item));
103+
else
104+
set->excluded_items.push_back(std::move(item));
105+
}
106+
else
107+
{
108+
set->excluded_items.push_back(PSOItem {
109+
.decl = decl,
110+
.exclusion_reason = ErrorMessage(decl->location,
111+
zpr::sprint("is not a generic function and does not accept type arguments", decl->name)),
112+
});
113+
}
114+
}
115+
116+
if(set->items.empty())
117+
{
118+
auto e = ErrorMessage(ts->loc(), zpr::sprint("Failed to resolve reference to generic entity '{}'", name));
119+
for(auto& f : set->excluded_items)
120+
e.addInfo(f.decl->location, zpr::sprint("Candidate failed: {}", f.exclusion_reason.string()));
121+
122+
return Err(std::move(e));
123+
}
124+
125+
return TCResult::ofRValue(std::move(set));
18126
}
19127
}

source/interp/polymorph.h

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace sap::interp::polymorph
1212
{
1313
ErrorOr<TCResult> tryInstantiateGenericFunction(Typechecker* ts,
1414
const Type* infer,
15+
const QualifiedId& name,
1516
std::vector<const cst::Declaration*> declarations,
1617
const std::vector<ast::FunctionCall::Arg>& explicit_args);
1718
}

source/interp/tc_result.h

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ namespace sap::interp
3636
std::unique_ptr<cst::Expr> take_expr() &&
3737
{
3838
assert(m_stmt->isExpr());
39-
assert(not m_type->isVoid());
4039
return std::unique_ptr<cst::Expr>(static_cast<cst::Expr*>(m_stmt.release()));
4140
}
4241

source/main.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ layout
9393

9494
misc
9595
----
96+
[ ] need a better ErrorMessage struct or something
97+
9698
[ ] XML metadata and/or declare PDF/A conformance
9799

98100
[ ] url annotations
@@ -103,7 +105,13 @@ misc
103105

104106
namespace sap
105107
{
106-
extern void set_draft_mode(bool);
108+
extern void set_draft_mode(bool _);
109+
110+
static stdfs::path s_invocation_cwd;
111+
stdfs::path getInvocationCWD()
112+
{
113+
return s_invocation_cwd;
114+
}
107115
}
108116

109117
int main(int argc, char** argv)
@@ -146,6 +154,7 @@ int main(int argc, char** argv)
146154

147155
// change directory to the input file so that all searches are (by default)
148156
// relative to it, regardless of our actual CWD
157+
sap::s_invocation_cwd = stdfs::current_path();
149158
stdfs::current_path(abs_filename.parent_path());
150159

151160
if(is_watching)

source/misc/error.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ namespace sap
4242
const char* colour_blue = coloured ? COLOUR_BLUE : "";
4343
const char* colour_reset = coloured ? COLOUR_RESET : "";
4444

45+
const auto shortened_filename = stdfs::path(loc.filename.str()).lexically_proximate(sap::getInvocationCWD());
46+
4547
zpr::fprintln(stderr, "{}{}:{} {}{}{}", colour_error, error_text, colour_reset, colour_black_bold, message,
4648
colour_reset);
47-
zpr::fprintln(stderr, "{} at:{} {}{}:{}:{}{}", colour_blue, colour_reset, colour_black_bold, loc.filename,
48-
loc.line + 1, loc.column + 1, colour_reset);
49+
zpr::fprintln(stderr, "{} at:{} {}{}:{}:{}{}", colour_blue, colour_reset, colour_black_bold,
50+
shortened_filename.generic_string(), loc.line + 1, loc.column + 1, colour_reset);
4951

5052
if(loc.is_builtin)
5153
return;

tests/lang/generics/01-basic.sap

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn f1[T](a: T)
2121

2222
f1[T: $int](10);
2323

24-
f1(a: 10);
24+
# f1(a: 10);
2525

2626
asdf()("69");
2727
# f1[$int](10);

0 commit comments

Comments
 (0)