module: disallow definition skipping
Invoking a non-composable, empty continuation during the right-hand side of a variable definition skips the definition --- while continuing the module body. The compiler assumes, however, that variable references later in the module do not need a check that the variable is undefined. Fix that mismatch by changing `module` to double-check that defined variables are really defined before continuing the module body. (The check and associated prompt are skipped in simple cases, such as function definitions.) A better choice is probably to move the prompt to the right-hand side of a definition, both in a module and at the top level. That's a much different language, though, so we should consider the point again in some future variant of Racket. Closes PR 14427
This commit is contained in:
parent
1273d0fbb3
commit
418ee07f4e
|
@ -245,8 +245,12 @@ its order within a given module). Then, expressions and definitions
|
|||
are evaluated in order as they appear within the module. Each
|
||||
evaluation of an expression or definition is wrapped with a
|
||||
continuation prompt (see @racket[call-with-continuation-prompt]) for
|
||||
the default continuation and using a prompt handler that re-aborts
|
||||
and propagates its argument to the next enclosing prompt.
|
||||
the default @tech{prompt tag} and using a prompt handler that re-aborts
|
||||
and propagates its argument to the next enclosing prompt. Each evaluation
|
||||
of a definition is followed, outside of the prompt, by a check that
|
||||
each of the definition's variables has a value; if the portion of the
|
||||
prompt-delimited continuation that installs values is skipped, then
|
||||
the @exnraise[exn:fail:contract:variable?].
|
||||
|
||||
Accessing a @tech{module-level variable} before it is defined signals
|
||||
a run-time error, just like accessing an undefined global variable.
|
||||
|
|
|
@ -1072,6 +1072,22 @@
|
|||
(set! c (compile m))))))
|
||||
(write c (open-output-bytes)))
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Check that skipping definitions (but continuing
|
||||
;; with the rest of a module body) is disallowed.
|
||||
|
||||
(module disallowed-definition-avoider racket/base
|
||||
|
||||
(define fail
|
||||
((call-with-continuation-prompt
|
||||
(lambda ()
|
||||
(call/cc values)))))
|
||||
|
||||
(error "no"))
|
||||
|
||||
(err/rt-test (dynamic-require ''disallowed-definition-avoider #f)
|
||||
exn:fail:contract:variable?)
|
||||
|
||||
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(report-errs)
|
||||
|
|
|
@ -5870,6 +5870,40 @@ void *scheme_module_run_finish(Scheme_Env *menv, Scheme_Env *env)
|
|||
(void)_scheme_call_with_prompt_multi(body_one_expr,
|
||||
scheme_make_raw_pair(save_prefix, body));
|
||||
scheme_resume_prefix(save_prefix);
|
||||
|
||||
/* Double-check that the definition-installing part of the
|
||||
continuation was not skipped. Otherwise, the compiler would
|
||||
not be able to assume that a variable reference that is
|
||||
lexically later (incuding a reference to an imported
|
||||
variable) always references a defined variable. Putting the
|
||||
prompt around a definition's RHS might be a better
|
||||
approach, but that would change the language (so mabe next
|
||||
time). */
|
||||
if (SAME_TYPE(SCHEME_TYPE(body), scheme_define_values_type)) {
|
||||
int vcnt, j;
|
||||
|
||||
vcnt = SCHEME_VEC_SIZE(body) - 1;
|
||||
for (j = 0; j < vcnt; j++) {
|
||||
Scheme_Object *var;
|
||||
Scheme_Prefix *toplevels;
|
||||
Scheme_Bucket *b;
|
||||
|
||||
var = SCHEME_VEC_ELS(body)[j+1];
|
||||
toplevels = (Scheme_Prefix *)MZ_RUNSTACK[SCHEME_TOPLEVEL_DEPTH(var)];
|
||||
b = (Scheme_Bucket *)toplevels->a[SCHEME_TOPLEVEL_POS(var)];
|
||||
|
||||
if (!b->val) {
|
||||
scheme_raise_exn(MZEXN_FAIL_CONTRACT_VARIABLE,
|
||||
b->key,
|
||||
"define-values: skipped variable definition;\n"
|
||||
" cannot continue without defining variable\n"
|
||||
" variable: %S\n"
|
||||
" in module: %D",
|
||||
(Scheme_Object *)b->key,
|
||||
menv->module->modsrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
scheme_ignore_result(_scheme_eval_linked_expr_multi(body));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user