summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-12-23 07:39:34 -0800
committerKaz Kylheku <kaz@kylheku.com>2021-12-23 07:39:34 -0800
commitda6829688c5ff6d294cb6157a84166837c880562 (patch)
treed0b32cfe41d62ac8e95d1b02329e793ca4d0bfe5
parent7eef2749ca3282585e65415712ebb810f2462a01 (diff)
downloadtxr-da6829688c5ff6d294cb6157a84166837c880562.tar.gz
txr-da6829688c5ff6d294cb6157a84166837c880562.tar.bz2
txr-da6829688c5ff6d294cb6157a84166837c880562.zip
eval: fix optional parameter bug from 2014.
This bug affects optional parameters which either have no default expression, or one that is nil. For instance x in (lambda (: (x nil))) or (lambda (: x)). When such a parameter is given the : symbol as an argument, it is not being bound, as if it weren't there. ((lambda (: x) x) :) -> ;; error: unbound variable x This issue is not a regression; it was introduced in the commit which introduced the colon convention to optionals, as well as init expressions and presence-indicating variables, commit 68c084269581f32f0a7b859446ae2efb6c6a26c0 made in February 2014. This might be the first instance of an interpreter bug being found that is not present in the compiler. * eval.c (bind_args): The idea here was that when the argument to an optional the colon keyword symbol, and the optional's initform is nil, we can skip the overhead of calling eval to get that initform's value. Unfortunately, the skip was extended over the code which binds the parameter. Only the eval can be skipped! * tests/012/lambda.tl: New test cases to cover this.
-rw-r--r--eval.c7
-rw-r--r--tests/012/lambda.tl14
2 files changed, 17 insertions, 4 deletions
diff --git a/eval.c b/eval.c
index b3a6e869..7517eb8b 100644
--- a/eval.c
+++ b/eval.c
@@ -973,11 +973,10 @@ static val bind_args(val env, val params, struct args *args, val ctx)
val present = nil;
if (arg == colon_k) {
- if (initform) {
+ if (initform)
initval = eval(initform, new_env, ctx);
- new_env = lex_or_dyn_bind_seq(&dyn_env_made, new_env,
- param, initval);
- }
+ new_env = lex_or_dyn_bind_seq(&dyn_env_made, new_env,
+ param, initval);
} else {
lex_or_dyn_bind(&dyn_env_made, new_env, param, arg);
present = t;
diff --git a/tests/012/lambda.tl b/tests/012/lambda.tl
index 5267e4ef..65a3738c 100644
--- a/tests/012/lambda.tl
+++ b/tests/012/lambda.tl
@@ -87,6 +87,20 @@
[(lambda (x y : (a 3) (b 4)) (list x y a b)) . vl] (a b c d)
[(lambda (x y : (a 3) (b 4)) (list x y a b)) 1 . vl] :error)
+(mtest
+ [(lambda (x : y) (list x y)) 1 :] (1 nil)
+ [(lambda (x : y z) (list x y z)) 1 :] (1 nil nil)
+ [(lambda (x : y z) (list x y z)) 1 2 :] (1 2 nil)
+ [(lambda (x : y z) (list x y z)) 1 nil :] (1 nil nil)
+ [(lambda (x : y z) (list x y z)) 1 nil nil] (1 nil nil))
+
+(mtest
+ [(lambda (x : (y nil)) (list x y)) 1 :] (1 nil)
+ [(lambda (x : (y nil) (z)) (list x y z)) 1 :] (1 nil nil)
+ [(lambda (x : (y nil) (z)) (list x y z)) 1 2 :] (1 2 nil)
+ [(lambda (x : (y nil) (z)) (list x y z)) 1 nil :] (1 nil nil)
+ [(lambda (x : (y nil) (z)) (list x y z)) 1 nil nil] (1 nil nil))
+
(defvarl vc '(: : : :))
(mtest