diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 2339fddfb2..3aea3b9b50 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -211,14 +211,14 @@ ;; Bind vars in body (defmacro let (bindings &rest body) - `((lambda :inline-only #t ,(apply first bindings) ,@body) + `((lambda :immediate #t ,(apply first bindings) ,@body) ,@(apply second bindings))) ;; Let, but recursive, allowing you to define variables in terms of others. (defmacro let* (bindings &rest body) (if (null? bindings) `(begin ,@body) - `((lambda :inline-only #t (,(caar bindings)) + `((lambda :immediate #t (,(caar bindings)) (let* ,(cdr bindings) ,@body)) ,(car (cdar bindings)) ) @@ -229,7 +229,7 @@ (defmacro mlet* (bindings &rest body) (if (null? bindings) `(begin ,@body) - `((lambda :inline-only #t (,(caar bindings)) + `((lambda :immediate #t (,(caar bindings)) (mlet* ,(cdr bindings) ,@body)) ,(car (cdar bindings)) ) @@ -503,7 +503,7 @@ `(let ((,(car bindings) ,(cadr bindings))) (while (not (null? ,(car bindings))) ,@body - + (set! ,(car bindings) (cdr ,(car bindings))) ) ) diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index be11cba058..3d4c902c79 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -131,10 +131,11 @@ class SymbolValueVal : public Val { */ class LambdaVal : public Val { public: - explicit LambdaVal(TypeSpec ts) : Val(std::move(ts)) {} + explicit LambdaVal(TypeSpec ts, bool immediate) : Val(std::move(ts)), is_immediate(immediate) {} std::string print() const override { return "lambda-" + lambda.debug_name; } FunctionEnv* func = nullptr; Lambda lambda; + bool is_immediate = false; RegVal* to_reg(const goos::Object& form, Env* fe) override; }; diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index e3c053319d..b342927bc5 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -101,15 +101,18 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest auto obj_env = env->file_env(); auto args = get_va(form, rest); if (args.unnamed.empty() || !args.unnamed.front().is_list() || - !args.only_contains_named({"name", "inline-only", "segment", "behavior"})) { + !args.only_contains_named({"name", "segment", "behavior", "immediate"})) { throw_compiler_error(form, "Invalid lambda form"); } + bool immediate = + args.has_named("immediate") && symbol_string(args.get_named("immediate")) != "#f"; + // allocate this lambda from the object file environment. This makes it safe for this to hold // on to references to this as an inlineable function even if the enclosing function fails. // for example, the top-level may (define some-func (lambda...)) and even if top-level fails, // we keep around a reference to some-func to be possibly inlined. - auto place = obj_env->alloc_val(get_none()->type()); + auto place = obj_env->alloc_val(get_none()->type(), immediate); auto& lambda = place->lambda; auto lambda_ts = m_ts.make_typespec("function"); @@ -141,9 +144,6 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest lambda.body = get_lambda_body(rest); // first is the argument list, rest is body place->func = nullptr; - bool inline_only = - args.has_named("inline-only") && symbol_string(args.get_named("inline-only")) != "#f"; - // pick default segment to store function in. int segment = fe->segment_for_static_data(); @@ -159,7 +159,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest } } - if (!inline_only) { + if (!immediate) { // compile a function! First create a unique name... std::string function_name = lambda.debug_name; if (function_name.empty()) { @@ -330,7 +330,7 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en // it's inlinable. However, we do not always inline an inlinable function by default if (kv->second.inline_by_default) { // inline when possible, so we should inline auto_inline = true; - auto* lv = env->function_env()->alloc_val(kv->second.type); + auto* lv = env->function_env()->alloc_val(kv->second.type, false); lv->lambda = kv->second.lambda; head = lv; } @@ -387,10 +387,16 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en auto head_as_inlined_lambda = dynamic_cast(head); if (head_as_inlined_lambda) { // yes, remember the lambda that contains and flag that we're inlining. - head_as_lambda = env->function_env()->alloc_val(head_as_inlined_lambda->lv.type); + head_as_lambda = + env->function_env()->alloc_val(head_as_inlined_lambda->lv.type, false); head_as_lambda->lambda = head_as_inlined_lambda->lv.lambda; got_inlined_lambda = true; } + } else { + // we got a lambda: but we don't want to use immediates by default: + if (!auto_inline && !head_as_lambda->is_immediate) { + head_as_lambda = nullptr; + } } } diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index 708281b14f..9ea91d24c0 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -273,7 +273,7 @@ Val* Compiler::generate_inspector_for_structure_type(const goos::Object& form, // add this function to the object file auto fe = env->function_env(); - auto method = fe->alloc_val(m_ts.make_typespec("function")); + auto method = fe->alloc_val(m_ts.make_typespec("function"), false); method->func = method_env.get(); auto obj_env_inspect = method_env->file_env(); obj_env_inspect->add_function(std::move(method_env)); @@ -344,7 +344,7 @@ Val* Compiler::generate_inspector_for_bitfield_type(const goos::Object& form, // add this function to the object file auto fe = env->function_env(); - auto method = fe->alloc_val(m_ts.make_typespec("function")); + auto method = fe->alloc_val(m_ts.make_typespec("function"), false); method->func = method_env.get(); auto obj_env_inspect = method_env->file_env(); obj_env_inspect->add_function(std::move(method_env)); @@ -450,7 +450,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ throw_compiler_error(form, "Method type must be a symbol, got {}", method_name.print()); } - auto place = fe->alloc_val(get_none()->type()); + auto place = fe->alloc_val(get_none()->type(), false); auto& lambda = place->lambda; auto lambda_ts = m_ts.make_typespec("function"); diff --git a/test/goalc/source_templates/control-statements/lambda-1.static.gc b/test/goalc/source_templates/control-statements/lambda-1.static.gc index b52a9418c9..168f667cb7 100644 --- a/test/goalc/source_templates/control-statements/lambda-1.static.gc +++ b/test/goalc/source_templates/control-statements/lambda-1.static.gc @@ -1 +1 @@ -((lambda :inline-only #t (x y z) y) 1 2 3) \ No newline at end of file +((lambda :immediate #t (x y z) y) 1 2 3) \ No newline at end of file