aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2019-08-31 20:13:28 +0100
committergingerBill <bill@gingerbill.org>2019-08-31 20:13:28 +0100
commitb9d3129fb3a4ba7ef49cea69d086a7f705819f2e (patch)
treed985a8be38dc2bbf05ea13d13e700ab5074b87ed /src
parentb311540b1672129e87a7249650a19cf11d2fccef (diff)
`where` clauses for procedure literals
Diffstat (limited to 'src')
-rw-r--r--src/check_decl.cpp33
-rw-r--r--src/check_expr.cpp115
-rw-r--r--src/checker.cpp8
-rw-r--r--src/parser.cpp50
-rw-r--r--src/parser.hpp2
-rw-r--r--src/tokenizer.cpp1
6 files changed, 185 insertions, 24 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 7e019d82b..156c874ce 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -936,7 +936,6 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
ptr_set_destroy(&entity_set);
-
for_array(j, pge->entities) {
Entity *p = pge->entities[j];
if (p->type == t_invalid) {
@@ -962,27 +961,40 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
defer (end_error_block());
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
- switch (kind) {
+ bool both_have_where_clauses = false;
+ if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) {
+ GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit);
+ GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit);
+ auto pl = &p->decl_info->proc_lit->ProcLit;
+ auto ql = &q->decl_info->proc_lit->ProcLit;
+
+ // Allow collisions if the procedures both have 'where' clauses and are both polymorphic
+ bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true);
+ bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true);
+ both_have_where_clauses = pw && qw;
+ }
+
+ if (!both_have_where_clauses) switch (kind) {
case ProcOverload_Identical:
- error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
+ error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
// case ProcOverload_CallingConvention:
- // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
+ // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// is_invalid = true;
// break;
case ProcOverload_ParamVariadic:
- error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
+ error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_ResultCount:
case ProcOverload_ResultTypes:
- error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in this scope", LIT(name));
+ error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_Polymorphic:
#if 0
- error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name));
+ error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in the procedure group '%.*s' which is not allowed", LIT(name), LIT(proc_group_name));
is_invalid = true;
#endif
break;
@@ -1163,6 +1175,13 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
}
+
+ bool where_clause_ok = evaluate_where_clauses(ctx, decl, true);
+ if (!where_clause_ok) {
+ // NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed
+ return;
+ }
+
check_open_scope(ctx, body);
{
for_array(i, using_entities) {
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 86cba0f94..4c8cd61a7 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -5470,6 +5470,74 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
return lhs;
}
+
+bool evaluate_where_clauses(CheckerContext *ctx, DeclInfo *decl, bool print_err) {
+ Ast *proc_lit = decl->proc_lit;
+ GB_ASSERT(proc_lit != nullptr);
+ GB_ASSERT(proc_lit->kind == Ast_ProcLit);
+
+ if (proc_lit->ProcLit.where_token.kind != Token_Invalid) {
+ auto &clauses = proc_lit->ProcLit.where_clauses;
+ for_array(i, clauses) {
+ Ast *clause = clauses[i];
+ Operand o = {};
+ check_expr(ctx, &o, clause);
+ if (o.mode != Addressing_Constant) {
+ if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ return false;
+ } else if (o.value.kind != ExactValue_Bool) {
+ if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation");
+ return false;
+ } else if (!o.value.value_bool) {
+ if (print_err) {
+ gbString str = expr_to_string(clause);
+ error(clause, "'where' clause evaluated to false:\n\t%s", str);
+ gb_string_free(str);
+
+ if (decl->scope != nullptr) {
+ isize print_count = 0;
+ for_array(j, decl->scope->elements.entries) {
+ Entity *e = decl->scope->elements.entries[j].value;
+ switch (e->kind) {
+ case Entity_TypeName: {
+ if (print_count == 0) error_line("\n\tWith the following definitions:\n");
+
+ gbString str = type_to_string(e->type);
+ error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str);
+ gb_string_free(str);
+ print_count += 1;
+ break;
+ }
+ case Entity_Constant: {
+ if (print_count == 0) error_line("\n\tWith the following definitions:\n");
+
+ gbString str = exact_value_to_string(e->Constant.value);
+ if (is_type_untyped(e->type)) {
+ error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str);
+ } else {
+ gbString t = type_to_string(e->type);
+ error_line("\t\t%.*s : %s : %s;\n", LIT(e->token.string), t, str);
+ gb_string_free(t);
+ }
+ gb_string_free(str);
+
+ print_count += 1;
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) {
ast_node(ce, CallExpr, call);
@@ -5710,11 +5778,26 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
- if (err == CallArgumentError_None) {
- valids[valid_count].index = i;
- valids[valid_count].score = data.score;
- valid_count++;
+ if (err != CallArgumentError_None) {
+ continue;
}
+ if (data.gen_entity != nullptr) {
+ Entity *e = data.gen_entity;
+ DeclInfo *decl = data.gen_entity->decl_info;
+ ctx.scope = decl->scope;
+ ctx.decl = decl;
+ ctx.proc_name = e->token.string;
+ ctx.curr_proc_decl = decl;
+ ctx.curr_proc_sig = e->type;
+
+ if (!evaluate_where_clauses(&ctx, decl, false)) {
+ continue;
+ }
+ }
+
+ valids[valid_count].index = i;
+ valids[valid_count].score = data.score;
+ valid_count++;
}
}
@@ -5822,7 +5905,29 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (proc->kind == Entity_Variable) {
sep = ":=";
}
- error_line("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column);
+ error_line("\t%.*s %s %s ", LIT(name), sep, pt);
+ if (proc->decl_info->proc_lit != nullptr) {
+ GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit);
+ auto *pl = &proc->decl_info->proc_lit->ProcLit;
+ if (pl->where_token.kind != Token_Invalid) {
+ error_line("\n\t\twhere ");
+ for_array(j, pl->where_clauses) {
+ Ast *clause = pl->where_clauses[j];
+ if (j != 0) {
+ error_line("\t\t ");
+ }
+ gbString str = expr_to_string(clause);
+ error_line("%s", str);
+ gb_string_free(str);
+
+ if (j != pl->where_clauses.count-1) {
+ error_line(",");
+ }
+ }
+ error_line("\n\t");
+ }
+ }
+ error_line("at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column);
// error_line("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score);
}
result_type = t_invalid;
diff --git a/src/checker.cpp b/src/checker.cpp
index b00b4bbac..8fe71b63c 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -3697,6 +3697,14 @@ void check_proc_info(Checker *c, ProcInfo pi) {
return;
}
+ if (pt->is_polymorphic && pt->is_poly_specialized) {
+ Entity *e = pi.decl->entity;
+ if ((e->flags & EntityFlag_Used) == 0) {
+ // NOTE(bill, 2019-08-31): It was never used, don't check
+ return;
+ }
+ }
+
bool bounds_check = (pi.tags & ProcTag_bounds_check) != 0;
bool no_bounds_check = (pi.tags & ProcTag_no_bounds_check) != 0;
diff --git a/src/parser.cpp b/src/parser.cpp
index e92489020..8490b0e00 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -144,6 +144,7 @@ Ast *clone_ast(Ast *node) {
case Ast_ProcLit:
n->ProcLit.type = clone_ast(n->ProcLit.type);
n->ProcLit.body = clone_ast(n->ProcLit.body);
+ n->ProcLit.where_clauses = clone_ast_array(n->ProcLit.where_clauses);
break;
case Ast_CompoundLit:
n->CompoundLit.type = clone_ast(n->CompoundLit.type);
@@ -612,11 +613,13 @@ Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast
return result;
}
-Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags) {
+Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_ProcLit);
result->ProcLit.type = type;
result->ProcLit.body = body;
result->ProcLit.tags = tags;
+ result->ProcLit.where_token = where_token;
+ result->ProcLit.where_clauses = where_clauses;
return result;
}
@@ -1827,15 +1830,41 @@ Ast *parse_operand(AstFile *f, bool lhs) {
}
Ast *type = parse_proc_type(f, token);
+ Token where_token = {};
+ Array<Ast *> where_clauses = {};
+ u64 tags = 0;
+
+ if (f->curr_token.kind == Token_where) {
+ where_token = expect_token(f, Token_where);
+ isize prev_level = f->expr_level;
+ f->expr_level = -1;
+ where_clauses = parse_rhs_expr_list(f);
+ f->expr_level = prev_level;
+ }
+
+ parse_proc_tags(f, &tags);
+ if ((tags & ProcTag_require_results) != 0) {
+ syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration");
+ tags &= ~ProcTag_require_results;
+ }
+ GB_ASSERT(type->kind == Ast_ProcType);
+ type->ProcType.tags = tags;
if (f->allow_type && f->expr_level < 0) {
+ if (tags != 0) {
+ syntax_error(token, "A procedure type cannot have suffix tags");
+ }
+ if (where_token.kind != Token_Invalid) {
+ syntax_error(where_token, "'where' clauses are not allowed on procedure types");
+ }
return type;
}
- u64 tags = type->ProcType.tags;
-
if (allow_token(f, Token_Undef)) {
- return ast_proc_lit(f, type, nullptr, tags);
+ if (where_token.kind != Token_Invalid) {
+ syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)");
+ }
+ return ast_proc_lit(f, type, nullptr, tags, where_token, where_clauses);
} else if (f->curr_token.kind == Token_OpenBrace) {
Ast *curr_proc = f->curr_proc;
Ast *body = nullptr;
@@ -1843,7 +1872,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
body = parse_body(f);
f->curr_proc = curr_proc;
- return ast_proc_lit(f, type, body, tags);
+ return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
} else if (allow_token(f, Token_do)) {
Ast *curr_proc = f->curr_proc;
Ast *body = nullptr;
@@ -1851,12 +1880,15 @@ Ast *parse_operand(AstFile *f, bool lhs) {
body = convert_stmt_to_body(f, parse_stmt(f));
f->curr_proc = curr_proc;
- return ast_proc_lit(f, type, body, tags);
+ return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
}
if (tags != 0) {
syntax_error(token, "A procedure type cannot have suffix tags");
}
+ if (where_token.kind != Token_Invalid) {
+ syntax_error(where_token, "'where' clauses are not allowed on procedure types");
+ }
return type;
}
@@ -2827,12 +2859,6 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) {
results = parse_results(f, &diverging);
u64 tags = 0;
- parse_proc_tags(f, &tags);
- if ((tags & ProcTag_require_results) != 0) {
- syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration");
- tags &= ~ProcTag_require_results;
- }
-
bool is_generic = false;
for_array(i, params->FieldList.list) {
diff --git a/src/parser.hpp b/src/parser.hpp
index 32398592e..26536fe56 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -229,6 +229,8 @@ enum StmtAllowFlag {
Ast *body; \
u64 tags; \
ProcInlining inlining; \
+ Token where_token; \
+ Array<Ast *> where_clauses; \
}) \
AST_KIND(CompoundLit, "compound literal", struct { \
Ast *type; \
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 618de54b9..d5e04aa1e 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -86,6 +86,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
TOKEN_KIND(Token_package, "package"), \
TOKEN_KIND(Token_typeid, "typeid"), \
TOKEN_KIND(Token_when, "when"), \
+ TOKEN_KIND(Token_where, "where"), \
TOKEN_KIND(Token_if, "if"), \
TOKEN_KIND(Token_else, "else"), \
TOKEN_KIND(Token_for, "for"), \