Skip to content

Commit d0a75ca

Browse files
committed
add import ... as ... syntax. closes #1255
1 parent f13e11c commit d0a75ca

12 files changed

+210
-48
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

+34-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ is shown for illustrative purposes:
1313
module MyModule
1414
using Lib
1515

16-
using BigLib: thing1, thing2
16+
using BigLib: thing1, thing2, thing3 as t3
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,37 @@ 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` or `using` can be renamed with the keyword `as`.
92+
This is useful for 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+
116+
`as` works with `using` only when a single identifier is brought into scope.
117+
For example `using CSV: read as rd` works, but `using CSV as C` does not, since it operates
118+
on all of the exported names in `CSV`.
119+
89120
### Modules and files
90121

91122
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

+21-6
Original file line numberDiff line numberDiff line change
@@ -1576,26 +1576,31 @@
15761576
e))
15771577

15781578
(define (parse-imports s word)
1579-
(let* ((first (parse-import s word))
1579+
(let* ((first (parse-import s word #f))
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
15881591
'()
15891592
(let ((ex (parse-comma-separated s (lambda (s)
1590-
(parse-import s word)))))
1593+
(parse-import s word from)))))
15911594
(if (eq? (peek-token s) ':)
15921595
(error (string "\":\" in \"" word "\" syntax can only be used "
15931596
"when importing a single module. "
15941597
"Split imports into multiple lines."))
15951598
ex)))))
15961599
(if from
15971600
`(,word (|:| ,first ,@rest))
1598-
(list* word first rest))))
1601+
(begin (if (and (eq? word 'using) (pair? first) (eq? (car first) 'as))
1602+
(error (string "invalid syntax \"using " (deparse-import-path (cadr first)) " as ...\"")))
1603+
(list* word first rest)))))
15991604

16001605
(define (parse-import-dots s)
16011606
(let loop ((l '())
@@ -1615,7 +1620,7 @@
16151620
(else
16161621
(cons (macrocall-to-atsym (parse-unary-prefix s)) l)))))
16171622

1618-
(define (parse-import s word)
1623+
(define (parse-import-path s word)
16191624
(let loop ((path (parse-import-dots s)))
16201625
(if (not (symbol-or-interpolate? (car path)))
16211626
(error (string "invalid \"" word "\" statement: expected identifier")))
@@ -1635,6 +1640,16 @@
16351640
(else
16361641
(cons '|.| (reverse path)))))))
16371642

1643+
(define (parse-import s word from)
1644+
(let ((path (parse-import-path s word)))
1645+
(if (eq? (peek-token s) 'as)
1646+
(begin
1647+
(if (and (not from) (eq? word 'using))
1648+
(error (string "invalid syntax \"using " (deparse-import-path path) " as ...\"")))
1649+
(take-token s)
1650+
`(as ,path ,(parse-unary-prefix s)))
1651+
path)))
1652+
16381653
;; parse comma-separated assignments, like "i=1:n,j=1:m,..."
16391654
(define (parse-comma-separated s what)
16401655
(let loop ((exprs '()))

src/julia.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -1474,8 +1474,9 @@ 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_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname);
1478+
JL_DLLEXPORT void jl_module_import(jl_module_t *to, jl_module_t *from, jl_sym_t *s);
1479+
JL_DLLEXPORT void jl_module_import_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname);
14791480
JL_DLLEXPORT void jl_module_export(jl_module_t *from, jl_sym_t *s);
14801481
JL_DLLEXPORT int jl_is_imported(jl_module_t *m, jl_sym_t *s);
14811482
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

+29-11
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,14 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m, jl_sym_
224224
}
225225
else {
226226
JL_UNLOCK_NOGC(&m->lock);
227-
jl_binding_t *b2 = jl_get_binding(b->owner, var);
227+
jl_binding_t *b2 = jl_get_binding(b->owner, b->name);
228228
if (b2 == NULL || b2->value == NULL)
229229
jl_errorf("invalid method definition: imported function %s.%s does not exist",
230-
jl_symbol_name(b->owner->name), jl_symbol_name(var));
230+
jl_symbol_name(b->owner->name), jl_symbol_name(b->name));
231231
// TODO: we might want to require explicitly importing types to add constructors
232232
if (!b->imported && !jl_is_type(b2->value)) {
233233
jl_errorf("error in method definition: function %s.%s must be explicitly imported to be extended",
234-
jl_symbol_name(b->owner->name), jl_symbol_name(var));
234+
jl_symbol_name(b->owner->name), jl_symbol_name(b->name));
235235
}
236236
return b2;
237237
}
@@ -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,22 @@ 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);
505+
}
506+
507+
JL_DLLEXPORT void jl_module_use_as(jl_module_t *to, jl_module_t *from, jl_sym_t *s, jl_sym_t *asname)
508+
{
509+
module_import_(to, from, s, asname, 0);
492510
}
493511

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

0 commit comments

Comments
 (0)