Skip to content

Commit f3e8d03

Browse files
committed
add import ... as ... syntax. closes #1255
1 parent 315ad46 commit f3e8d03

12 files changed

+167
-39
lines changed

NEWS.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ New language features
1313
* The library name passed to `ccall` or `@ccall` can now be an expression involving
1414
global variables and function calls. The expression will be evaluated the first
1515
time the `ccall` executes ([#36458]).
16+
* The syntax `import A as B` (plus `import A: x as y`, etc.) can now be used to
17+
rename imported modules and identifiers ([#1255]).
1618

1719
Language changes
1820
----------------

base/show.jl

+8-1
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,10 @@ function show_generator(io, ex::Expr, indent, quote_level)
14441444
end
14451445
end
14461446

1447-
function valid_import_path(@nospecialize ex)
1447+
function valid_import_path(@nospecialize(ex), allow_as = true)
1448+
if allow_as && is_expr(ex, :as) && length((ex::Expr).args) == 2
1449+
ex = (ex::Expr).args[1]
1450+
end
14481451
return is_expr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args)
14491452
end
14501453

@@ -1992,6 +1995,10 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
19921995
first = false
19931996
show_import_path(io, a, quote_level)
19941997
end
1998+
elseif head === :as && nargs == 2 && valid_import_path(args[1], false)
1999+
show_import_path(io, args[1], quote_level)
2000+
print(io, " as ")
2001+
show_unquoted(io, args[2], indent, 0, quote_level)
19952002
elseif head === :meta && nargs >= 2 && args[1] === :push_loc
19962003
print(io, "# meta: location ", join(args[2:end], " "))
19972004
elseif head === :meta && nargs == 1 && args[1] === :pop_loc

doc/src/manual/modules.md

+29-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ using Lib
1515

1616
using BigLib: thing1, thing2
1717

18-
import Base.show
18+
import Base.show, Base.print as pr
1919

2020
export MyType, foo
2121

@@ -26,7 +26,7 @@ end
2626
bar(x) = 2x
2727
foo(a::MyType) = bar(a.x) + 1
2828

29-
show(io::IO, a::MyType) = print(io, "MyType $(a.x)")
29+
show(io::IO, a::MyType) = pr(io, "MyType $(a.x)")
3030
end
3131
```
3232

@@ -86,6 +86,33 @@ functions into the current workspace:
8686
| `import MyModule.x, MyModule.p` | `x` and `p` | `x` and `p` |
8787
| `import MyModule: x, p` | `x` and `p` | `x` and `p` |
8888

89+
### Import renaming
90+
91+
An identifier brought into scope by `import` can be renamed using the keyword `as`. This is useful for
92+
working around name conflicts as well as for shortening names.
93+
For example, `Base` exports the function name `read`, but the CSV.jl package also provides `CSV.read`.
94+
If we are going to invoke CSV reading many times, it would be convenient to drop the `CSV.` qualifier.
95+
But then it is ambiguous whether we are referring to `Base.read` or `CSV.read`:
96+
97+
```julia
98+
julia> read;
99+
100+
julia> import CSV.read
101+
WARNING: ignoring conflicting import of CSV.read into Main
102+
```
103+
104+
Renaming provides a solution:
105+
106+
```julia
107+
julia> import CSV.read as rd
108+
```
109+
110+
Imported packages themselves can also be renamed:
111+
112+
```julia
113+
import BenchmarkTools as bt
114+
```
115+
89116
### Modules and files
90117

91118
Files and file names are mostly unrelated to modules; modules are associated only with module

src/ast.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jl_sym_t *exc_sym; jl_sym_t *error_sym;
4444
jl_sym_t *new_sym; jl_sym_t *using_sym;
4545
jl_sym_t *splatnew_sym;
4646
jl_sym_t *const_sym; jl_sym_t *thunk_sym;
47-
jl_sym_t *foreigncall_sym;
47+
jl_sym_t *foreigncall_sym; jl_sym_t *as_sym;
4848
jl_sym_t *global_sym; jl_sym_t *list_sym;
4949
jl_sym_t *dot_sym; jl_sym_t *newvar_sym;
5050
jl_sym_t *boundscheck_sym; jl_sym_t *inbounds_sym;
@@ -363,6 +363,7 @@ void jl_init_common_symbols(void)
363363
thunk_sym = jl_symbol("thunk");
364364
toplevel_sym = jl_symbol("toplevel");
365365
dot_sym = jl_symbol(".");
366+
as_sym = jl_symbol("as");
366367
colon_sym = jl_symbol(":");
367368
boundscheck_sym = jl_symbol("boundscheck");
368369
inbounds_sym = jl_symbol("inbounds");

src/ast.scm

+18-15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@
4646
(string ":" (deparse e))
4747
(deparse e)))
4848

49+
(define (deparse-import-path e)
50+
(cond ((and (pair? e) (eq? (car e) '|.|))
51+
(let loop ((lst (cdr e))
52+
(ndots 0))
53+
(if (or (null? lst)
54+
(not (eq? (car lst) '|.|)))
55+
(string (string.rep "." ndots)
56+
(string.join (map deparse lst) "."))
57+
(loop (cdr lst) (+ ndots 1)))))
58+
((and (pair? e) (eq? (car e) ':))
59+
(string (deparse-import-path (cadr e)) ": "
60+
(string.join (map deparse-import-path (cddr e)) ", ")))
61+
(else
62+
(string e))))
63+
4964
(define (deparse e (ilvl 0))
5065
(cond ((or (symbol? e) (number? e)) (string e))
5166
((string? e) (print-to-string e))
@@ -71,6 +86,8 @@
7186
(if (length= e 2)
7287
(string (car e) (deparse (cadr e)))
7388
(string (deparse (cadr e)) " " (car e) " " (deparse (caddr e)))))
89+
((eq? (car e) 'as)
90+
(string (deparse-import-path (cadr e)) " as " (deparse (caddr e))))
7491
(else
7592
(case (car e)
7693
((null) "nothing")
@@ -209,21 +226,7 @@
209226
"end"))
210227
;; misc syntax forms
211228
((import using)
212-
(define (deparse-path e)
213-
(cond ((and (pair? e) (eq? (car e) '|.|))
214-
(let loop ((lst (cdr e))
215-
(ndots 0))
216-
(if (or (null? lst)
217-
(not (eq? (car lst) '|.|)))
218-
(string (string.rep "." ndots)
219-
(string.join (map deparse lst) "."))
220-
(loop (cdr lst) (+ ndots 1)))))
221-
((and (pair? e) (eq? (car e) ':))
222-
(string (deparse-path (cadr e)) ": "
223-
(string.join (map deparse-path (cddr e)) ", ")))
224-
(else
225-
(string e))))
226-
(string (car e) " " (string.join (map deparse-path (cdr e)) ", ")))
229+
(string (car e) " " (string.join (map deparse-import-path (cdr e)) ", ")))
227230
((global local export) (string (car e) " " (string.join (map deparse (cdr e)) ", ")))
228231
((const) (string "const " (deparse (cadr e))))
229232
((top) (deparse (cadr e)))

src/julia-parser.scm

+16-3
Original file line numberDiff line numberDiff line change
@@ -1578,10 +1578,13 @@
15781578
(define (parse-imports s word)
15791579
(let* ((first (parse-import s word))
15801580
(next (peek-token s))
1581-
(from (and (eq? next ':) (not (ts:space? s))))
1581+
(from (and (eq? next ':) (not (ts:space? s))
1582+
(or (not (and (pair? first) (eq? (car first) 'as)))
1583+
(error (string "invalid syntax \"" word " " (deparse first) ":\"")))))
15821584
(done (cond ((or from (eqv? next #\,))
15831585
(begin (take-token s) #f))
1584-
((or (eq? next '|.|)
1586+
;; TODO: this seems to be wrong; figure out if it's needed
1587+
#;((or (eq? next '|.|)
15851588
(eqv? (string.sub (string next) 0 1) ".")) #f)
15861589
(else #t)))
15871590
(rest (if done
@@ -1615,7 +1618,7 @@
16151618
(else
16161619
(cons (macrocall-to-atsym (parse-unary-prefix s)) l)))))
16171620

1618-
(define (parse-import s word)
1621+
(define (parse-import-path s word)
16191622
(let loop ((path (parse-import-dots s)))
16201623
(if (not (symbol-or-interpolate? (car path)))
16211624
(error (string "invalid \"" word "\" statement: expected identifier")))
@@ -1635,6 +1638,16 @@
16351638
(else
16361639
(cons '|.| (reverse path)))))))
16371640

1641+
(define (parse-import s word)
1642+
(let ((path (parse-import-path s word)))
1643+
(if (eq? (peek-token s) 'as)
1644+
(begin
1645+
(if (eq? word 'using)
1646+
(error "\"as\" can only be used with \"import\", not \"using\""))
1647+
(take-token s)
1648+
`(as ,path ,(parse-unary-prefix s)))
1649+
path)))
1650+
16381651
;; parse comma-separated assignments, like "i=1:n,j=1:m,..."
16391652
(define (parse-comma-separated s what)
16401653
(let loop ((exprs '()))

src/julia.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1474,8 +1474,8 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b JL_ROOTING_ARGUMENT, jl_
14741474
JL_DLLEXPORT void jl_declare_constant(jl_binding_t *b);
14751475
JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from);
14761476
JL_DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
1477-
JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from,
1478-
jl_sym_t *s);
1477+
JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
1478+
JL_DLLEXPORT void jl_module_import_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname);
14791479
JL_DLLEXPORT void jl_module_export(jl_module_t *from, jl_sym_t *s);
14801480
JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *s);
14811481
JL_DLLEXPORT int jl_module_exports_p(jl_module_t *m, jl_sym_t *var) JL_NOTSAFEPOINT;

src/julia_internal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ extern jl_sym_t *new_sym; extern jl_sym_t *using_sym;
12411241
extern jl_sym_t *splatnew_sym;
12421242
extern jl_sym_t *pop_exception_sym;
12431243
extern jl_sym_t *const_sym; extern jl_sym_t *thunk_sym;
1244-
extern jl_sym_t *foreigncall_sym;
1244+
extern jl_sym_t *foreigncall_sym; extern jl_sym_t *as_sym;
12451245
extern jl_sym_t *global_sym; extern jl_sym_t *list_sym;
12461246
extern jl_sym_t *dot_sym; extern jl_sym_t *newvar_sym;
12471247
extern jl_sym_t *boundscheck_sym; extern jl_sym_t *inbounds_sym;

src/module.c

+21-8
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_
248248
return b;
249249
}
250250

251-
static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s,
251+
static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname,
252252
int explici);
253253

254254
typedef struct _modstack_t {
@@ -321,14 +321,14 @@ static jl_binding_t *jl_get_binding_(jl_module_t *m, jl_sym_t *var, modstack_t *
321321
// do a full import to prevent the result of this lookup
322322
// from changing, for example if this var is assigned to
323323
// later.
324-
module_import_(m, b->owner, var, 0);
324+
module_import_(m, b->owner, var, var, 0);
325325
return b;
326326
}
327327
return NULL;
328328
}
329329
JL_UNLOCK(&m->lock);
330330
if (b->owner != m)
331-
return jl_get_binding_(b->owner, var, &top);
331+
return jl_get_binding_(b->owner, b->name, &top);
332332
return b;
333333
}
334334

@@ -394,7 +394,7 @@ JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *s)
394394
}
395395

396396
// NOTE: we use explici since explicit is a C++ keyword
397-
static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, int explici)
397+
static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname, int explici)
398398
{
399399
jl_binding_t *b = jl_get_binding(from, s);
400400
if (b == NULL) {
@@ -421,19 +421,27 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, int
421421
}
422422

423423
JL_LOCK(&to->lock);
424-
jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&to->bindings, s);
424+
jl_binding_t **bp = (jl_binding_t**)ptrhash_bp(&to->bindings, asname);
425425
jl_binding_t *bto = *bp;
426426
if (bto != HT_NOTFOUND) {
427427
if (bto == b) {
428428
// importing a binding on top of itself. harmless.
429429
}
430+
else if (bto->name != s) {
431+
JL_UNLOCK(&to->lock);
432+
jl_printf(JL_STDERR,
433+
"WARNING: ignoring conflicting import of %s.%s into %s\n",
434+
jl_symbol_name(from->name), jl_symbol_name(s),
435+
jl_symbol_name(to->name));
436+
return;
437+
}
430438
else if (bto->owner == b->owner) {
431439
// already imported
432440
bto->imported = (explici!=0);
433441
}
434442
else if (bto->owner != to && bto->owner != NULL) {
435443
// already imported from somewhere else
436-
jl_binding_t *bval = jl_get_binding(to, s);
444+
jl_binding_t *bval = jl_get_binding(to, asname);
437445
if (bval->constp && bval->value && b->constp && b->value == bval->value) {
438446
// equivalent binding
439447
bto->imported = (explici!=0);
@@ -483,12 +491,17 @@ static void module_import_(jl_module_t *to, jl_module_t *from, jl_sym_t *s, int
483491

484492
JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s)
485493
{
486-
module_import_(to, from, s, 1);
494+
module_import_(to, from, s, s, 1);
495+
}
496+
497+
JL_DLLEXPORT void jl_module_import_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname)
498+
{
499+
module_import_(to, from, s, asname, 1);
487500
}
488501

489502
JL_DLLEXPORT void jl_module_use(jl_module_t *to, jl_module_t *from, jl_sym_t *s)
490503
{
491-
module_import_(to, from, s, 0);
504+
module_import_(to, from, s, s, 0);
492505
}
493506

494507
JL_DLLEXPORT void jl_module_using(jl_module_t *to, jl_module_t *from)

src/toplevel.c

+21-6
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,10 @@ static jl_method_instance_t *method_instance_for_thunk(jl_code_info_t *src, jl_m
542542
return li;
543543
}
544544

545-
static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import)
545+
static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym_t *asname)
546546
{
547547
assert(m);
548-
jl_sym_t *name = import->name;
548+
jl_sym_t *name = asname ? asname : import->name;
549549
jl_binding_t *b;
550550
if (jl_binding_resolved_p(m, name)) {
551551
b = jl_get_binding(m, name);
@@ -691,7 +691,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int
691691
if (m == jl_main_module && name == NULL) {
692692
// TODO: for now, `using A` in Main also creates an explicit binding for `A`
693693
// This will possibly be extended to all modules.
694-
import_module(m, u);
694+
import_module(m, u, NULL);
695695
}
696696
}
697697
}
@@ -716,15 +716,30 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int
716716
name = NULL;
717717
jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)a)->args, &name, "import");
718718
if (name == NULL) {
719-
import_module(m, import);
719+
import_module(m, import, NULL);
720720
}
721721
else {
722722
jl_module_import(m, import, name);
723723
}
724+
continue;
724725
}
725-
else {
726-
jl_eval_errorf(m, "syntax: malformed \"import\" statement");
726+
else if (jl_is_expr(a) && ((jl_expr_t*)a)->head == as_sym && jl_expr_nargs(a) == 2 &&
727+
jl_is_expr(jl_exprarg(a, 0)) && ((jl_expr_t*)jl_exprarg(a, 0))->head == dot_sym) {
728+
jl_sym_t *asname = (jl_sym_t*)jl_exprarg(a, 1);
729+
if (jl_is_symbol(asname)) {
730+
jl_expr_t *path = (jl_expr_t*)jl_exprarg(a, 0);
731+
name = NULL;
732+
jl_module_t *import = eval_import_path(m, from, ((jl_expr_t*)path)->args, &name, "import");
733+
if (name == NULL) {
734+
import_module(m, import, asname);
735+
}
736+
else {
737+
jl_module_import_as(m, import, name, asname);
738+
}
739+
continue;
740+
}
727741
}
742+
jl_eval_errorf(m, "syntax: malformed \"import\" statement");
728743
}
729744
JL_GC_POP();
730745
return jl_nothing;

test/show.jl

+5
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ end
205205
@test_repr "import A.B.C: a, x, y.z"
206206
@test_repr "import ..A: a, x, y.z"
207207
@test_repr "import A.B, C.D"
208+
@test_repr "import A as B"
209+
@test_repr "import A.x as y"
210+
@test_repr "import A: x as y"
211+
@test_repr "import A.B: x, y as z"
212+
@test_repr "import A.B: x, y as z, a.b as c, xx"
208213

209214
# keyword args (issue #34023 and #32775)
210215
@test_repr "f(a, b=c)"

0 commit comments

Comments
 (0)