Skip to content

Commit cff04c0

Browse files
authored
Merge pull request #1787 from r-lib/feature/api-find-var
Progress towards conformance of environment API
2 parents 24efceb + 86e362d commit cff04c0

File tree

9 files changed

+166
-87
lines changed

9 files changed

+166
-87
lines changed

R/c-lib.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,5 +405,5 @@ vec_resize <- function(x, n) {
405405
# walk.c
406406

407407
sexp_iterate <- function(x, fn) {
408-
do.call(".Call", list(ffi_sexp_iterate, x, fn))
408+
do.call(".Call", list(get("ffi_sexp_iterate"), x, fn))
409409
}

R/utils.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,5 @@ pkg_url_bug <- function(pkg) {
368368

369369
NULL
370370
}
371+
372+
is_windows <- tolower(.Platform$OS.type) == "windows"

src/internal/dots-ellipsis.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ r_obj* ffi_ellipsis_find_dots(r_obj* env) {
66
r_abort("`env` is a not an environment.");
77
}
88

9-
r_obj* dots = KEEP(r_env_find(env, r_syms.dots));
10-
if (dots == r_syms.unbound) {
9+
// `r_env_get()` triggers missing argument errors
10+
if (r_env_has_missing(env, r_syms.dots)) {
11+
return r_syms.missing;
12+
}
13+
14+
if (!r_env_has(env, r_syms.dots)) {
1115
r_abort("No `...` found.");
1216
}
1317

14-
FREE(1);
15-
return dots;
18+
return r_env_get(env, r_syms.dots);
1619
}
1720

1821
r_obj* ffi_ellipsis_dots(r_obj* env) {

src/internal/env-binding.c

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ r_obj* ffi_env_get(r_obj* env,
2727
return env_get_sym(env, sym, c_inherit, last, closure_env);
2828
}
2929

30+
// This util is a little more complex than it would be by calling `getVar()`
31+
// directly because it evaluates `default` lazily
3032
static
3133
r_obj* env_get_sym(r_obj* env,
3234
r_obj* sym,
@@ -37,25 +39,19 @@ r_obj* env_get_sym(r_obj* env,
3739
r_abort("`last` must be an environment.");
3840
}
3941

40-
r_obj* out;
42+
bool unbound;
4143
if (inherit) {
4244
if (last == r_null) {
43-
out = r_env_find_anywhere(env, sym);
45+
unbound = !r_env_has_anywhere(env, sym);
4446
} else {
45-
out = r_env_find_until(env, sym, last);
47+
unbound = !r_env_has_until(env, sym, last);
4648
}
4749
} else {
48-
out = r_env_find(env, sym);
50+
unbound = !r_env_has(env, sym);
4951
}
5052

51-
if (r_typeof(out) == R_TYPE_promise) {
52-
KEEP(out);
53-
out = r_eval(out, r_envs.empty);
54-
FREE(1);
55-
}
56-
57-
if (out == r_syms.unbound) {
58-
if (r_env_find(closure_env, r_sym("default")) == r_missing_arg) {
53+
if (unbound) {
54+
if (r_env_has_missing(closure_env, r_sym("default"))) {
5955
struct r_pair args[] = {
6056
{ r_sym("nm"), KEEP(r_str_as_character(r_sym_string(sym))) }
6157
};
@@ -67,10 +63,18 @@ r_obj* env_get_sym(r_obj* env,
6763
r_stop_unreachable();
6864
}
6965

70-
out = r_eval(r_sym("default"), closure_env);
66+
return r_eval(r_sym("default"), closure_env);
7167
}
7268

73-
return out;
69+
if (inherit) {
70+
if (last == r_null) {
71+
return r_env_get_anywhere(env, sym);
72+
} else {
73+
return r_env_get_until(env, sym, last);
74+
}
75+
} else {
76+
return r_env_get(env, sym);
77+
}
7478
}
7579

7680
r_obj* ffi_env_get_list(r_obj* env,
@@ -145,42 +149,46 @@ static void env_poke_lazy(r_obj* env, r_obj* sym, r_obj* value, r_obj* eval_env)
145149
static void env_poke_active(r_obj* env, r_obj* sym, r_obj* fn, r_obj* eval_env);
146150
static r_obj* env_get(r_obj* env, r_obj* sym);
147151

148-
r_obj* ffi_env_poke(r_obj* env, r_obj* nm, r_obj* value, r_obj* inherit, r_obj* create) {
152+
r_obj* ffi_env_poke(r_obj* env, r_obj* nm, r_obj* value, r_obj* ffi_inherit, r_obj* ffi_create) {
149153
if (r_typeof(env) != R_TYPE_environment) {
150154
r_abort("`env` must be an environment.");
151155
}
152156
if (!r_is_string(nm)) {
153157
r_abort("`nm` must be a string.");
154158
}
155-
if (!r_is_bool(inherit)) {
159+
if (!r_is_bool(ffi_inherit)) {
156160
r_abort("`inherit` must be a logical value.");
157161
}
158-
if (!r_is_bool(create)) {
162+
if (!r_is_bool(ffi_create)) {
159163
r_abort("`create` must be a logical value.");
160164
}
161165

162-
bool c_inherit = r_lgl_get(inherit, 0);
163-
bool c_create = r_lgl_get(create, 0);
166+
bool inherit = r_lgl_get(ffi_inherit, 0);
167+
bool create = r_lgl_get(ffi_create, 0);
164168
r_obj* sym = r_str_as_symbol(r_chr_get(nm, 0));
165169

166-
r_obj* old;
167-
if (c_inherit) {
168-
old = r_env_find_anywhere(env, sym);
170+
bool unbound;
171+
if (inherit) {
172+
unbound = !r_env_has_anywhere(env, sym);
169173
} else {
170-
old = r_env_find(env, sym);
174+
unbound = !r_env_has(env, sym);
171175
}
172176

173-
bool absent = (old == r_syms.unbound);
174-
if (absent) {
175-
if (!c_create) {
177+
r_obj* old;
178+
if (unbound) {
179+
if (!create) {
176180
r_abort("Can't find existing binding in `env` for \"%s\".",
177181
r_sym_c_string(sym));
178182
}
179183
old = rlang_zap;
184+
} else if (inherit) {
185+
old = r_env_get_anywhere(env, sym);
186+
} else {
187+
old = r_env_get(env, sym);
180188
}
181189
KEEP(old);
182190

183-
if (c_inherit && !absent) {
191+
if (inherit && !unbound) {
184192
while (env != r_envs.empty) {
185193
if (r_env_has(env, sym)) {
186194
break;
@@ -322,17 +330,13 @@ void env_poke_active(r_obj* env, r_obj* sym, r_obj* fn, r_obj* eval_env) {
322330

323331
static
324332
r_obj* env_get(r_obj* env, r_obj* sym) {
325-
r_obj* out = r_env_find(env, sym);
326-
327-
if (out == r_syms.unbound) {
333+
if (!r_env_has(env, sym)) {
328334
return rlang_zap;
329335
}
330336

331-
if (r_typeof(out) == R_TYPE_promise) {
332-
KEEP(out);
333-
out = r_eval(out, r_envs.base);
334-
FREE(1);
337+
if (r_env_has_missing(env, sym)) {
338+
return r_missing_arg;
335339
}
336340

337-
return out;
341+
return r_env_get(env, sym);
338342
}

src/rlang/decl/env-decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ r_obj* env2list_call;
3232
static
3333
r_obj* list2env_call;
3434

35+
static
36+
r_obj* missing_prim;
37+
3538
#if R_VERSION < R_Version(4, 0, 0)
3639
static
3740
r_obj* env_as_list_compat(r_obj* env, r_obj* out);

src/rlang/env.c

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,19 @@ r_obj* rlang_ns_env;
66

77

88
r_obj* r_ns_env(const char* pkg) {
9-
r_obj* ns = r_env_find(R_NamespaceRegistry, r_sym(pkg));
10-
if (ns == r_syms.unbound) {
9+
r_obj* pkg_sym = r_sym(pkg);
10+
if (!r_env_has(R_NamespaceRegistry, pkg_sym)) {
1111
r_abort("Can't find namespace `%s`", pkg);
1212
}
13-
return ns;
14-
}
15-
16-
static
17-
r_obj* ns_env_get(r_obj* env, const char* name) {
18-
r_obj* obj = KEEP(r_env_find(env, r_sym(name)));
19-
20-
// Can be a promise to a lazyLoadDBfetch() call
21-
if (r_typeof(obj) == R_TYPE_promise) {
22-
obj = r_eval(obj, r_envs.empty);
23-
}
24-
if (obj != r_syms.unbound) {
25-
FREE(1);
26-
return obj;
27-
}
2813

29-
// Trigger object not found error
30-
r_eval(r_sym(name), env);
31-
r_stop_unreachable();
14+
return r_env_get(R_NamespaceRegistry, pkg_sym);
3215
}
16+
3317
r_obj* r_base_ns_get(const char* name) {
34-
return ns_env_get(r_envs.base, name);
18+
return r_env_get(r_envs.base, r_sym(name));
3519
}
36-
37-
3820
r_obj* rlang_ns_get(const char* name) {
39-
return ns_env_get(rlang_ns_env, name);
21+
return r_env_get(rlang_ns_env, r_sym(name));
4022
}
4123

4224

@@ -265,27 +247,50 @@ bool r_env_inherits(r_obj* env, r_obj* ancestor, r_obj* top) {
265247
if (env == ancestor) {
266248
return true;
267249
}
268-
env = r_env_parent(env);;
250+
env = r_env_parent(env);
269251
}
270252

271253
return env == ancestor;
272254
}
273255

274-
r_obj* r_env_find_until(r_obj* env, r_obj* sym, r_obj* last) {
256+
static
257+
r_obj* env_until(r_obj* env, r_obj* sym, r_obj* last) {
275258
r_obj* stop = r_envs.empty;
276259
if (last != r_envs.empty) {
277260
stop = r_env_parent(last);
278261
}
279262

280-
r_obj* out = r_syms.unbound;
281-
while (out == r_syms.unbound && env != r_envs.empty && env != stop) {
282-
out = r_env_find(env, sym);
283-
env = r_env_parent(env);
263+
while (true) {
264+
if (env == r_envs.empty || r_env_has(env, sym)) {
265+
return env;
266+
}
267+
268+
r_obj* next = r_env_parent(env);
269+
if (next == r_envs.empty || next == stop) {
270+
return env;
271+
}
272+
273+
env = next;
284274
}
275+
}
285276

286-
return out;
277+
r_obj* r_env_get_until(r_obj* env, r_obj* sym, r_obj* last) {
278+
env = env_until(env, sym, last);
279+
return r_env_get(env, sym);
287280
}
288281

282+
bool r_env_has_until(r_obj* env, r_obj* sym, r_obj* last) {
283+
env = env_until(env, sym, last);
284+
return r_env_has(env, sym);
285+
}
286+
287+
bool r_env_has_missing(r_obj* env, r_obj* sym) {
288+
// That's a special primitive so no need to protect `sym`
289+
r_obj* call = KEEP(r_call2(missing_prim, sym));
290+
r_obj* out = r_eval(call, env);
291+
FREE(1);
292+
return r_as_bool(out);
293+
}
289294

290295
void r_init_rlang_ns_env(void) {
291296
rlang_ns_env = r_ns_env("rlang");
@@ -300,6 +305,8 @@ void r_init_library_env(void) {
300305
new_env__size_node = r_node_cdr(new_env__parent_node);
301306
#endif
302307

308+
missing_prim = r_parse_eval("missing", r_envs.base);
309+
303310
env2list_call = r_parse("as.list.environment(x, all.names = TRUE)");
304311
r_preserve(env2list_call);
305312

@@ -351,3 +358,6 @@ r_obj* env2list_call = NULL;
351358

352359
static
353360
r_obj* list2env_call = NULL;
361+
362+
static
363+
r_obj* missing_prim = NULL;

src/rlang/env.h

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include "cnd.h"
88
#include "globals.h"
99
#include "obj.h"
10+
#include "rlang.h"
11+
#include "sym.h"
12+
13+
#define RLANG_USE_R_EXISTS (R_VERSION < R_Version(4, 2, 0))
1014

1115

1216
extern r_obj* r_methods_ns_env;
@@ -54,11 +58,52 @@ static inline
5458
r_obj* r_env_find_anywhere(r_obj* env, r_obj* sym) {
5559
return Rf_findVar(sym, env);
5660
}
57-
r_obj* r_env_find_until(r_obj* env, r_obj* sym, r_obj* last);
5861

62+
#if 1 || R_VERSION < R_Version(4, 5, 0)
63+
// We currently can't use `R_getVar()` which:
64+
// 1. Throws if not found
65+
// 2. Throws if argument is the missing arg
66+
// 3. Evaluates promises
67+
// Our operators have to return missing arguments.
68+
static inline
69+
r_obj* r_env_get(r_obj* env, r_obj* sym) {
70+
r_obj* out = r_env_find(env, sym);
71+
72+
if (out == r_syms.unbound) {
73+
r_abort("object '%s' not found", r_sym_c_string(sym));
74+
}
75+
76+
if (r_typeof(out) == R_TYPE_promise) {
77+
return Rf_eval(out, env);
78+
}
5979

60-
// TODO: Enable `R_existsVarInFrame()` when R 4.2 is out
61-
#define RLANG_USE_R_EXISTS (1 || R_VERSION < R_Version(4, 2, 0))
80+
return out;
81+
}
82+
83+
static inline
84+
r_obj* r_env_get_anywhere(r_obj* env, r_obj* sym) {
85+
r_obj* out = r_env_find_anywhere(env, sym);
86+
87+
if (out == r_syms.unbound) {
88+
r_abort("object '%s' not found", r_sym_c_string(sym));
89+
}
90+
91+
return out;
92+
}
93+
#else
94+
static inline
95+
r_obj* r_env_get(r_obj* env, r_obj* sym) {
96+
return R_getVar(sym, env, FALSE);
97+
}
98+
99+
static inline
100+
r_obj* r_env_get_anywhere(r_obj* env, r_obj* sym) {
101+
return R_getVar(sym, env, TRUE);
102+
}
103+
#endif
104+
105+
r_obj* r_env_get_until(r_obj* env, r_obj* sym, r_obj* last);
106+
bool r_env_has_until(r_obj* env, r_obj* sym, r_obj* last);
62107

63108
static inline
64109
bool r_env_has(r_obj* env, r_obj* sym) {
@@ -76,10 +121,18 @@ bool r_env_has_anywhere(r_obj* env, r_obj* sym) {
76121
bool r__env_has_anywhere(r_obj*, r_obj*);
77122
return r__env_has_anywhere(env, sym);
78123
#else
79-
return TODO();
124+
while (env != r_envs.empty) {
125+
if (r_env_has(env, sym)) {
126+
return true;
127+
}
128+
env = r_env_parent(env);
129+
}
130+
return false;
80131
#endif
81132
}
82133

134+
bool r_env_has_missing(r_obj* env, r_obj* sym);
135+
83136
r_obj* r_ns_env(const char* pkg);
84137
r_obj* r_base_ns_get(const char* name);
85138

0 commit comments

Comments
 (0)