diff --git a/collects/scribblings/raco/zo-struct.scrbl b/collects/scribblings/raco/zo-struct.scrbl index 67645d8df8..5f8e73451e 100644 --- a/collects/scribblings/raco/zo-struct.scrbl +++ b/collects/scribblings/raco/zo-struct.scrbl @@ -174,7 +174,8 @@ structures that are produced by @racket[zo-parse] and consumed by [dummy toplevel?] [lang-info (or/c #f (vector/c module-path? symbol? any/c))] [internal-context (or/c #f #t stx?)] - [submodules (listof mod?)])]{ + [pre-submodules (listof mod?)] + [post-submodules (listof mod?)])]{ Represents a @racket[module] declaration. The @racket[provides] and @racket[requires] lists are each an diff --git a/collects/scribblings/reference/eval.scrbl b/collects/scribblings/reference/eval.scrbl index a398182355..89aaac93b2 100644 --- a/collects/scribblings/reference/eval.scrbl +++ b/collects/scribblings/reference/eval.scrbl @@ -76,18 +76,37 @@ and its lexical context is not enriched before it is passed to the @tech{evaluation handler}.} -@defparam[current-load proc (path? (or/c symbol? #f) . -> . any)]{ +@defparam[current-load proc (path? (or/c #f + symbol? + (cons/c (or/c #f symbol?) + (non-empty-listof symbol?))) + . -> . + any)]{ A parameter that determines the current @deftech{load handler} to load top-level forms from a file. The @tech{load handler} is called by @racket[load], @racket[load-relative], @racket[load/cd], and the default @tech{compiled-load handler}. -A load handler takes two arguments: a path (see -@secref["pathutils"]) and an expected module name. The expected -module name is a symbol when the call is to load a module declaration -in response to a @racket[require] (in which case the file should -contain a module declaration), or @racket[#f] for any other load. +A load handler takes two arguments: a path (see @secref["pathutils"]) +and an expected module name. The expected module name is a symbol or a +list when the call is to load a module declaration in response to a +@racket[require] (in which case the file should contain a module +declaration), or @racket[#f] for any other load. + +When loading a module from a stream that starts with a compiled module +that contains submodules, the load handler should load only the +requested module, where a symbol as the load handler's indicates the +root module and a list indicates a submodule whose path relative to +the root module is given by the @racket[cdr] of the list. The list +starts with @racket[#f] when a submodule should be loaded @emph{only} +if it can be loaded independently (i.e., from compiled form---never +from source); if the submodule cannot be loaded independently, the +load handler should return without loading from a file. When the +expected module name is a list that starts with a symbol, the root +module and any other submodules can be loaded from the given file, +which might be from source, and the load handler still should not +complain if the expected submodule is not found. The default load handler reads forms from the file in @racket[read-syntax] mode with line-counting enabled for the file @@ -211,7 +230,11 @@ Like @racket[load-extension], but resolves @racket[file] using @racket[current-load-relative-directory] like @racket[load-relative].} -@defparam[current-load/use-compiled proc (path? (or/c symbol? #f) . -> . any)]{ +@defparam[current-load/use-compiled proc (path? (or/c #f + symbol? + (cons/c (or/c #f symbol?) + (non-empty-listof symbol?))) + . -> . any)]{ A parameter that determines the current @deftech{compiled-load handler} to load from a file that may have a compiled form. The diff --git a/collects/scribblings/reference/module-reflect.scrbl b/collects/scribblings/reference/module-reflect.scrbl index bfaf7270f6..ab0289a047 100644 --- a/collects/scribblings/reference/module-reflect.scrbl +++ b/collects/scribblings/reference/module-reflect.scrbl @@ -135,7 +135,12 @@ A parameter that determines a module name that is used when evaluating a @racket[module] declaration (when the parameter value is not @racket[#f]). In that case, the @racket[_id] from the @racket[module] declaration is ignored, and the parameter's value is used as the name -of the declared module.} +of the declared module. + +When declaring @tech{submodules}, @racket[current-module-declare-name] +determines the name used for the submodule's root module, while its +submodule path relative to the root module is unaffected.} + @defparam[current-module-declare-source src (or/c symbol? (and/c path? complete-path?) #f)]{ @@ -215,7 +220,7 @@ resolved name can depend on the value of @defproc[(module-path-index-split [mpi module-path-index?]) - (values (or/c module-path? #f) + (values (or/c module-path? path? #f) (or/c module-path-index? resolved-module-path? #f))]{ Returns two values: a module path, and a base @tech{module path index} @@ -230,7 +235,7 @@ A @racket[#f] for the first result implies a @racket[#f] for the second result, and means that @racket[mpi] represents ``self'' (see above).} -@defproc[(module-path-index-join [path (or/c module-path? #f)] +@defproc[(module-path-index-join [path (or/c module-path? path? #f)] [mpi (or/c module-path-index? resolved-module-path? #f)]) module-path-index?]{ @@ -245,11 +250,36 @@ declaration, @racket[#f] otherwise. See also @racket[current-compile].} -@defproc[(module-compiled-name [compiled-module-code compiled-module-expression?]) - symbol?]{ +@defproc*[([(module-compiled-name [compiled-module-code compiled-module-expression?]) + (or/c symbol? (cons/c symbol? (listof symbol?)))] + [(module-compiled-name [compiled-module-code compiled-module-expression?] + [name (or/c symbol? (cons/c symbol? (listof symbol?)))]) + compiled-module-expression?])]{ -Takes a module declaration in compiled form and returns a symbol for -the module's declared name.} +Takes a module declaration in compiled form and either gets the +module's declared name (when @racket[name] is not provided) or returns +a revised module declaration with the given @racket[name]. + +The name is a symbol for a top-level module, and it list of symbols +for a @tech{submodule}, where a list reflects the submodule path to +the module starting with the top-level module's declared name.} + + +@defproc*[([(module-compiled-submodules [compiled-module-code compiled-module-expression?] + [non-star? any/c]) + symbol?] + [(module-compiled-submodules [compiled-module-code compiled-module-expression?] + [non-star? any/c] + [submodules (listof compiled-module-expression?)]) + compiled-module-expression?])]{ + +Takes a module declaration in compiled form and either gets the +module's @tech{submodules} (when @racket[submodules] is not provided) or +returns a revised module declaration with the given +@racket[submodules]. The @racket[pre-module?] argument determines +whether the result or new submodule list corresponds to +@racket[module] declarations (when @racket[non-star?] is true) +or @racket[module*] declarations (when @racket[non-star?] is @racket[#f]).} @defproc[(module-compiled-imports [compiled-module-code compiled-module-expression?]) @@ -385,7 +415,8 @@ more than the namespace's @tech{base phase}.} @defproc[(module-declared? - [mod (or/c module-path? path? resolved-module-path?)] + [mod (or/c module-path? path? module-path-index? + resolved-module-path?)] [load? any/c #f]) boolean?]{ @@ -395,12 +426,16 @@ in the current namespace, @racket[#f] otherwise. If @racket[load?] is @racket[#t] and @racket[mod] is not a @tech{resolved module path}, the module is loaded in the process of -resolving @racket[mod] (as for @racket[dynamic-require] -and other functions).} +resolving @racket[mod] (as for @racket[dynamic-require] and other +functions). Checking for the declaration of a @tech{submodule} does +not trigger an exception if the submodule cannot be loaded because +it does not exist, either within a root module that does exist or +because the root module does not exist.} @defproc[(module->language-info - [mod (or/c module-path? path? resolved-module-path?)] + [mod (or/c module-path? path? module-path-index? + resolved-module-path?)] [load? any/c #f]) (or/c #f (vector/c module-path? symbol? any/c))]{ @@ -417,7 +452,8 @@ implementation as compiled code.} @defproc[(module->imports - [mod (or/c module-path? path? resolved-module-path?)]) + [mod (or/c module-path? path? module-path-index? + resolved-module-path?)]) (listof (cons/c (or/c exact-integer? #f) (listof module-path-index?)))]{ diff --git a/collects/scribblings/reference/reader.scrbl b/collects/scribblings/reference/reader.scrbl index 16c4ebdc0a..e178321088 100644 --- a/collects/scribblings/reference/reader.scrbl +++ b/collects/scribblings/reference/reader.scrbl @@ -837,8 +837,13 @@ of alphanumeric ASCII, @litchar{+}, @litchar{-}, @litchar{_}, and/or @litchar{/} characters terminated by @racketlink[char-whitespace?]{whitespace} or an end-of-file. The sequence must not start or end with @litchar{/}. A sequence -@litchar{#lang }@nonterm{name} is equivalent to -@litchar{#reader }@nonterm{name}@litchar{/lang/reader}. Note +@litchar{#lang }@nonterm{name} is equivalent to either +@litchar{#reader (submod }@nonterm{name}@litchar{ reader)} or +@litchar{#reader }@nonterm{name}@litchar{/lang/reader}, where the +former is tried first guarded by a @racket[module-declared?] +check (but after filtering by +@racket[current-reader-guard], so both are passed to the +value of @racket[current-reader-guard] if the latter is used). Note that the terminating whitespace (if any) is not consumed before the external reading procedure is called. diff --git a/collects/scribblings/reference/stx-ops.scrbl b/collects/scribblings/reference/stx-ops.scrbl index 087c917d56..32669deec7 100644 --- a/collects/scribblings/reference/stx-ops.scrbl +++ b/collects/scribblings/reference/stx-ops.scrbl @@ -81,12 +81,12 @@ opposed to @tech{syntax object}s inserted by macros.} @defproc[(syntax-source-module [stx syntax?] [source? any/c #f]) - (or/c module-path-index? symbol? path? #f)]{ + (or/c module-path-index? symbol? path? resolved-module-path? #f)]{ Returns an indication of the module whose source contains @racket[stx], or @racket[#f] if @racket[stx] has no source module. If @racket[source?] is @racket[#f], then result is a module path index or -symbol (see @secref["modpathidx"]); if @racket[source?] is true, the +symbol (see @secref["modpathidx"]) or a @tech{resolved module path}; if @racket[source?] is true, the result is a path or symbol corresponding to the loaded module's source in the sense of @racket[current-module-declare-source].} diff --git a/collects/scribblings/reference/syntax.scrbl b/collects/scribblings/reference/syntax.scrbl index e8bb80871e..1c4d0e3929 100644 --- a/collects/scribblings/reference/syntax.scrbl +++ b/collects/scribblings/reference/syntax.scrbl @@ -1039,13 +1039,16 @@ multiple symbolic names.} (#,(racketidfont "prefix-all-except") prefix-id raw-module-path id ...) (#,(racketidfont "rename") raw-module-path local-id exported-id)] - [raw-module-path (#,(racketidfont "quote") id) - rel-string - (#,(racketidfont "lib") rel-string ...) - id - (#,(racketidfont "file") string) - (#,(racketidfont "planet") rel-string - (user-string pkg-string vers ...))])]{ + [raw-module-path (#,(racketidfont "submod") raw-root-module-path id ...+) + (#,(racketidfont "submod") "." id ...+)] + [raw-root-module-path (#,(racketidfont "quote") id) + rel-string + (#,(racketidfont "lib") rel-string ...) + id + (#,(racketidfont "file") string) + (#,(racketidfont "planet") rel-string + (user-string pkg-string vers ...)) + literal-path])]{ The primitive import form, to which @racket[require] expands. A @racket[raw-require-spec] is similar to a @racket[_require-spec] in a @@ -1063,7 +1066,14 @@ identifiers in reverse order compared to @racket[rename-in]. For most @racket[raw-require-spec]s, the lexical context of the @racket[raw-require-spec] determines the context of introduced identifiers. The exception is the @racketidfont{rename} sub-form, -where the lexical context of the @racket[local-id] is preserved.} +where the lexical context of the @racket[local-id] is preserved. + +A @racket[literal-path] as a @racket[raw-root-module-path] corresponds +to a path in the sense of @racket[path?]. Since path values are never +produced by @racket[read-syntax], they appear only in programmatically +constructed expressions. They also appear naturally as arguments to +functions such as @racket[namespace-require], with otherwise take a +quoted @racket[raw-module-spec].} @defform/subs[(#%provide raw-provide-spec ...) diff --git a/collects/tests/racket/module.rktl b/collects/tests/racket/module.rktl index 02e09f28ce..41a2ca9f2a 100644 --- a/collects/tests/racket/module.rktl +++ b/collects/tests/racket/module.rktl @@ -639,6 +639,24 @@ (require (for-meta -1 (only-in racket cons) (only-in r5rs cons))))) (lambda (exn) (regexp-match? #rx"phase -1" (exn-message exn)))) + +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Check renames and lifts: + +(module post-ex-rename-example-1 racket/base + (require (for-syntax racket/base)) + (provide go) + (define-syntax (go stx) + (syntax-local-lift-module-end-declaration + #'(define-stuff)) + #'(define-syntax (define-stuff stx) + #'(define x #f)))) + +(module post-ex-rename-example-2 racket/base + (require 'post-ex-rename-example-1) + (go)) + + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (report-errs) diff --git a/collects/tests/racket/submodule.rktl b/collects/tests/racket/submodule.rktl index 51088568e7..f820f86c7e 100644 --- a/collects/tests/racket/submodule.rktl +++ b/collects/tests/racket/submodule.rktl @@ -45,6 +45,27 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(module subm-example-9 racket/base + (define x '(1)) + (provide x) + (module* a #f + (require racket/list) + (define z (last x)) + (provide z) + (define-syntax-rule (as-last) last) + (provide as-last))) + +(test '(1) dynamic-require ''subm-example-9 'x) +(test 1 dynamic-require '(submod 'subm-example-9 a) 'z) + +(module subm-example-use-9 racket/base + (require (submod 'subm-example-9 a)) + (define x ((as-last) '(1 2 3))) + (provide x)) +(test 3 dynamic-require ''subm-example-use-9 'x) + +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (let ([o (open-output-bytes)]) (write (compile '(module subm-example-0 racket/base (define x 1) @@ -262,4 +283,35 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(module subm-example-11 racket/base + (require (for-syntax racket/base)) + (define (listify x) (list x)) + + (define-syntax (add-submodule stx) + (with-syntax (#;[(define-b-module) (generate-temporaries '(define-b))]) + (syntax-local-lift-module-end-declaration + #'(module* a #f + (provide a) + (define a (listify 'a)))) + (syntax-local-lift-module-end-declaration + #'(define-b-module)) + #'(define-syntax (define-b-module stx) + #'(module* b #f + (define b 'b) + (provide b))))) + (add-submodule) + (provide add-submodule)) + +(test '(a) dynamic-require '(submod 'subm-example-11 a) 'a) +(test 'b dynamic-require '(submod 'subm-example-11 b) 'b) + +(module subm-example-12 racket/base + (require 'subm-example-11) + (add-submodule)) + +(test '(a) dynamic-require '(submod 'subm-example-12 a) 'a) +(test 'b dynamic-require '(submod 'subm-example-12 b) 'b) + +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (report-errs) diff --git a/doc/release-notes/racket/HISTORY.txt b/doc/release-notes/racket/HISTORY.txt index 66954fd955..9b834d3ca7 100644 --- a/doc/release-notes/racket/HISTORY.txt +++ b/doc/release-notes/racket/HISTORY.txt @@ -1,5 +1,5 @@ Version 5.2.900.1 -Add submodules, including `module*' +Add submodules, including module*, module-compiled-submodules Change resolver protocol so that declaration notify is always issued Change load/use-compiled handler to work with submodules compiler/zo-struct: added pre-submodules and post-submodules field to diff --git a/src/racket/src/module.c b/src/racket/src/module.c index 9768789c22..cea0473a5b 100644 --- a/src/racket/src/module.c +++ b/src/racket/src/module.c @@ -5989,8 +5989,8 @@ static Scheme_Object *jit_vector(Scheme_Object *orig_l, int in_vec, int jit) static Scheme_Object *do_module_clone(Scheme_Object *data, int jit) { Scheme_Module *m = (Scheme_Module *)data; - Scheme_Object *l1, **naya = NULL; - int j, i; + Scheme_Object *l1, *l2, *pre_submods, *post_submods, *sm, **naya = NULL; + int j, i, submod_changed; Resolve_Prefix *rp; rp = scheme_prefix_eval_clone(m->prefix); @@ -6013,8 +6013,33 @@ static Scheme_Object *do_module_clone(Scheme_Object *data, int jit) } } + pre_submods = m->pre_submodules; + post_submods = m->post_submodules; + submod_changed = 0; + + for (j = 0; j < 2; j++) { + l1 = (j ? post_submods : pre_submods); + if (l1 && !SCHEME_NULLP(l1)) { + l2 = scheme_null; + while (!SCHEME_NULLP(l1)) { + sm = do_module_clone(SCHEME_CAR(l1), jit); + if (!SAME_OBJ(sm, SCHEME_CAR(l1))) + submod_changed = 1; + l2 = scheme_make_pair(sm, l2); + l1 = SCHEME_CDR(l1); + } + if (submod_changed) { + l2 = scheme_reverse(l2); + if (j) + post_submods = l2; + else + pre_submods = l2; + } + } + } + if (!naya) { - if (SAME_OBJ(rp, m->prefix)) + if (SAME_OBJ(rp, m->prefix) && !submod_changed) return data; naya = m->bodies; } @@ -6024,6 +6049,9 @@ static Scheme_Object *do_module_clone(Scheme_Object *data, int jit) m->bodies = naya; m->prefix = rp; + m->pre_submodules = pre_submods; + m->post_submodules = post_submods; + return (Scheme_Object *)m; } @@ -6837,6 +6865,18 @@ static void propagate_imports(Module_Begin_Expand_State *bxs, } } +Scheme_Object *reverse_and_add_rename(Scheme_Object *fm, Scheme_Object *post_ex_rn) +{ + Scheme_Object *l2 = scheme_null; + + while (!SCHEME_NULLP(fm)) { + l2 = scheme_make_pair(scheme_add_rename(SCHEME_CAR(fm), post_ex_rn), + l2); + fm = SCHEME_CDR(fm); + } + return l2; +} + static Scheme_Object *stx_sym(Scheme_Object *name, Scheme_Object *_genv) { return scheme_tl_id_sym((Scheme_Env *)_genv, name, NULL, 2, NULL, NULL); @@ -7302,12 +7342,16 @@ static Scheme_Object *do_module_begin(Scheme_Object *orig_form, Scheme_Comp_Env Resolve_Info *ri; Scheme_Object *o; int max_let_depth; + int use_jit; /* Since we optimize & resolve the module here, it won't need to be optimized and resolved later. The resolve pass sets m->comp_prefix to NULL, which is how optimize & resolve know to avoid re-optimizing and re-resolving. */ + o = scheme_get_param(scheme_current_config(), MZCONFIG_USE_JIT); + use_jit = SCHEME_TRUEP(o); + oi = scheme_optimize_info_create(env->prefix); scheme_optimize_info_enforce_const(oi, rec[drec].comp_flags & COMP_ENFORCE_CONSTS); if (!(rec[drec].comp_flags & COMP_CAN_INLINE)) @@ -7322,6 +7366,11 @@ static Scheme_Object *do_module_begin(Scheme_Object *orig_form, Scheme_Comp_Env max_let_depth = scheme_resolve_info_max_let_depth(ri); o = scheme_sfs(o, NULL, max_let_depth); + if (use_jit) + o = scheme_jit_expr(o); + else + o = scheme_eval_clone(o); + (void)do_module_execute(o, env->genv, 0, 1, root_module_name); } @@ -7663,7 +7712,7 @@ static Scheme_Object *do_module_begin_at_phase(Scheme_Object *form, Scheme_Comp_ e = scheme_reverse(e); if (expand_ends) { fm = scheme_frame_get_end_statement_lifts(xenv); - fm = scheme_reverse(fm); + fm = reverse_and_add_rename(fm, post_ex_rn); if (!SCHEME_NULLP(e)) fm = scheme_append(fm, e); maybe_has_lifts = 0; @@ -8057,7 +8106,7 @@ static Scheme_Object *do_module_begin_at_phase(Scheme_Object *form, Scheme_Comp_ e = scheme_reverse(e); if (expand_ends) { fm = scheme_frame_get_end_statement_lifts(xenv); - fm = scheme_reverse(fm); + fm = reverse_and_add_rename(fm, post_ex_rn); if (!SCHEME_NULLP(e)) fm = scheme_append(fm, e); maybe_has_lifts = 0; @@ -10216,6 +10265,11 @@ void add_single_require(Scheme_Module_Exports *me, /* from module */ leads to generated local names). */ context_marks = scheme_stx_extract_marks(mark_src); has_context = !SCHEME_NULLP(context_marks); + if (!has_context) { + if (SCHEME_TRUEP(scheme_stx_moduleless_env(mark_src))) { + has_context = 1; + } + } if (has_context) { if (all_simple) *all_simple = 0;