From aa029fe8d9d48477f0be27fa79f8c541451a8a0a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 May 2020 13:38:06 +0100 Subject: [PATCH] Add `"pure"` procedure types --- examples/demo/demo.odin | 17 +++++++++++++++ src/check_decl.cpp | 6 ++++-- src/check_expr.cpp | 46 ++++++++++++++++++++++++++++------------- src/check_stmt.cpp | 11 ++++++++++ src/check_type.cpp | 5 ++++- src/checker.hpp | 1 + src/ir_print.cpp | 1 + src/parser.cpp | 1 + src/parser.hpp | 1 + 9 files changed, 72 insertions(+), 17 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index e6882fbcc..f908e6ecd 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1961,6 +1961,22 @@ relative_data_types :: proc() { fmt.println(rel_slice[1]); } +pure_procedures :: proc() { + fmt.println("\n#pure procedures"); + + square :: proc "pure" (x: int) -> int { + return x*x + 1; + } + + do_math :: proc "pure" (x: int) -> int { + // Only "pure" procedure calls are allowed within a "pure" procedure + return square(x) + 1; + } + + x := do_math(5); + fmt.println("do_math(5) ==", x); +} + main :: proc() { when true { the_basics(); @@ -1993,5 +2009,6 @@ main :: proc() { union_maybe(); explicit_context_definition(); relative_data_types(); + pure_procedures(); } } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f629a0903..5b343c5cb 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1174,11 +1174,14 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty CheckerContext new_ctx = *ctx_; CheckerContext *ctx = &new_ctx; + GB_ASSERT(type->kind == Type_Proc); + ctx->scope = decl->scope; ctx->decl = decl; ctx->proc_name = proc_name; ctx->curr_proc_decl = decl; ctx->curr_proc_sig = type; + ctx->curr_proc_calling_convention = type->Proc.calling_convention; ast_node(bs, BlockStmt, body); @@ -1187,7 +1190,6 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty defer (array_free(&using_entities)); { - GB_ASSERT(type->kind == Type_Proc); if (type->Proc.param_count > 0) { TypeTuple *params = &type->Proc.params->Tuple; for_array(i, params->variables) { @@ -1242,7 +1244,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty // 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 1fb364c1d..d2190d8cd 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1165,6 +1165,11 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ if (e->flags & EntityFlag_Value) { o->mode = Addressing_Value; } + if (c->curr_proc_calling_convention == ProcCC_Pure) { + if (e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Pkg)) { + error(n, "Global variables are not allowed within a \"pure\" procedure, got '%.*s'", LIT(e->token.string)); + } + } break; case Entity_Procedure: @@ -7434,6 +7439,14 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr } } + { + if (c->curr_proc_calling_convention == ProcCC_Pure) { + if (pt->kind == Type_Proc && pt->Proc.calling_convention != ProcCC_Pure) { + error(call, "Only \"pure\" procedure calls are allowed within a \"pure\" procedure"); + } + } + } + #if 0 if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { init_core_context(c->checker); @@ -7755,23 +7768,28 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(i, Implicit, node) switch (i->kind) { case Token_context: - if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { - error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl); - return kind; - } + { + if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) { + error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl); + return kind; + } + if (c->curr_proc_calling_convention == ProcCC_Pure) { + error(node, "'context' is not allowed within a \"pure\" procedure"); + } else { + if (unparen_expr(c->assignment_lhs_hint) == node) { + c->scope->flags |= ScopeFlag_ContextDefined; + } - if (unparen_expr(c->assignment_lhs_hint) == node) { - c->scope->flags |= ScopeFlag_ContextDefined; - } + if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { + error(node, "'context' has not been defined within this scope"); + // Continue with value + } + } - if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { - error(node, "'context' has not been defined within this scope"); - // Continue with value + init_core_context(c->checker); + o->mode = Addressing_Context; + o->type = t_context; } - - init_core_context(c->checker); - o->mode = Addressing_Context; - o->type = t_context; break; default: diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6672ea204..e357d366d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1301,6 +1301,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { case_end; case_ast_node(as, AssignStmt, node); + if (ctx->curr_proc_calling_convention == ProcCC_Pure) { + error(node, "Assignment statements are not allowed within a \"pure\" procedure"); + // Continue + } + switch (as->op.kind) { case Token_Eq: { // a, b, c = 1, 2, 3; // Multisided @@ -2027,6 +2032,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_arity_match(ctx, vd); check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); + if (ctx->curr_proc_calling_convention == ProcCC_Pure) { + if (vd->values.count == 0) { + error(node, "Variable declarations without assignment are not allowed within \"pure\" procedures"); + } + } + for (isize i = 0; i < entity_count; i++) { Entity *e = entities[i]; diff --git a/src/check_type.cpp b/src/check_type.cpp index 3829694a0..d826860a6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2452,6 +2452,7 @@ void set_procedure_abi_types(gbAllocator allocator, Type *type) { switch (type->Proc.calling_convention) { case ProcCC_Odin: case ProcCC_Contextless: + case ProcCC_Pure: if (is_type_pointer(new_type) & !is_type_pointer(e->type)) { e->flags |= EntityFlag_ImplicitReference; } @@ -2548,7 +2549,9 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, type->Proc.has_named_results = first->token.string != ""; } - + if (result_count == 0 && cc == ProcCC_Pure) { + error(proc_type_node, "\"pure\" procedures must have at least 1 return value"); + } bool optional_ok = (pt->tags & ProcTag_optional_ok) != 0; diff --git a/src/checker.hpp b/src/checker.hpp index 409bc9de2..161c6b72e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -297,6 +297,7 @@ struct CheckerContext { String proc_name; DeclInfo * curr_proc_decl; Type * curr_proc_sig; + ProcCallingConvention curr_proc_calling_convention; bool in_proc_sig; ForeignContext foreign_context; gbAllocator allocator; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 59bbadbfb..9a7b1e113 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1434,6 +1434,7 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven switch (cc) { case ProcCC_Odin: ir_write_str_lit(f, ""); break; case ProcCC_Contextless: ir_write_str_lit(f, ""); break; + case ProcCC_Pure: ir_write_str_lit(f, ""); break; // case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; case ProcCC_CDecl: ir_write_str_lit(f, ""); break; case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break; diff --git a/src/parser.cpp b/src/parser.cpp index 8bde4a403..6a6a4ecd0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2987,6 +2987,7 @@ Ast *parse_results(AstFile *f, bool *diverging) { ProcCallingConvention string_to_calling_convention(String s) { if (s == "odin") return ProcCC_Odin; if (s == "contextless") return ProcCC_Contextless; + if (s == "pure") return ProcCC_Pure; if (s == "cdecl") return ProcCC_CDecl; if (s == "c") return ProcCC_CDecl; if (s == "stdcall") return ProcCC_StdCall; diff --git a/src/parser.hpp b/src/parser.hpp index 00e299ba5..9cf8ebbf2 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -176,6 +176,7 @@ enum ProcCallingConvention { ProcCC_Invalid = 0, ProcCC_Odin, ProcCC_Contextless, + ProcCC_Pure, ProcCC_CDecl, ProcCC_StdCall, ProcCC_FastCall,