Skip to content

Commit d485b45

Browse files
committed
Teach C level vec_ptype_common() how to opt out of ptype finalization
And ensure that we always finalize on the way out from `vec_ptype_common()`, even with a user supplied `ptype`!
1 parent 68657fe commit d485b45

File tree

11 files changed

+178
-24
lines changed

11 files changed

+178
-24
lines changed

R/type.R

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,18 @@ vec_ptype_common <- function(
120120
vec_ptype_common_params <- function(
121121
...,
122122
.ptype = NULL,
123+
.finalise = TRUE,
123124
.fallback_opts = fallback_opts(),
124125
.arg = "",
125126
.call = caller_env()
126127
) {
127-
.External2(ffi_ptype_common_params, list2(...), .ptype, .fallback_opts)
128+
.External2(
129+
ffi_ptype_common_params,
130+
list2(...),
131+
.ptype,
132+
.finalise,
133+
.fallback_opts
134+
)
128135
}
129136

130137
vec_ptype_common_fallback <- function(

src/bind.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ r_obj* vec_rbind(r_obj* xs,
7474
ptype = vec_ptype_common(
7575
xs,
7676
ptype,
77+
PTYPE_FINALISE_DEFAULT,
7778
S3_FALLBACK_true,
7879
p_arg,
7980
error_call
@@ -498,6 +499,7 @@ r_obj* vec_cbind(r_obj* xs,
498499
r_obj* type = KEEP(vec_ptype_common(
499500
xs_data_frames,
500501
ptype,
502+
PTYPE_FINALISE_DEFAULT,
501503
S3_FALLBACK_false,
502504
p_arg,
503505
error_call

src/cast.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ r_obj* vec_cast_common_opts(r_obj* xs,
257257
r_obj* type = KEEP(vec_ptype_common(
258258
xs,
259259
to,
260+
PTYPE_FINALISE_DEFAULT,
260261
opts->s3_fallback,
261262
opts->p_arg,
262263
opts->call

src/init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ extern r_obj* ffi_new_data_frame(r_obj*);
392392
static
393393
const R_ExternalMethodDef ExtEntries[] = {
394394
{"ffi_ptype_common", (DL_FUNC) &ffi_ptype_common, 2},
395-
{"ffi_ptype_common_params", (DL_FUNC) &ffi_ptype_common_params, 3},
395+
{"ffi_ptype_common_params", (DL_FUNC) &ffi_ptype_common_params, 4},
396396
{"ffi_size_common", (DL_FUNC) &ffi_size_common, 3},
397397
{"ffi_recycle_common", (DL_FUNC) &ffi_recycle_common, 2},
398398
{"ffi_cast_common", (DL_FUNC) &ffi_cast_common, 2},

src/list-combine.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,7 @@ r_obj* list_combine_common_class_fallback(
809809
vec_ptype_common(
810810
xs,
811811
ptype,
812+
PTYPE_FINALISE_DEFAULT,
812813
s3_fallback,
813814
p_xs_arg,
814815
error_call
@@ -1806,6 +1807,8 @@ r_obj* ptype_common_with_default(
18061807
ptype = KEEP(vec_ptype_common(
18071808
xs,
18081809
ptype,
1810+
// TODO!: This should be `false`
1811+
PTYPE_FINALISE_true,
18091812
s3_fallback,
18101813
p_xs_arg,
18111814
error_call

src/ptype-common.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ r_obj* ffi_ptype_common(r_obj* ffi_call, r_obj* op, r_obj* args, r_obj* env) {
1818
struct r_lazy xs_arg_lazy = { .x = syms.dot_arg, .env = env };
1919
struct vctrs_arg xs_arg = new_lazy_arg(&xs_arg_lazy);
2020

21+
// User calls to `vec_ptype_common()` are always finalised and never fall back
22+
const enum ptype_finalise finalise = PTYPE_FINALISE_DEFAULT;
23+
const enum s3_fallback s3_fallback = S3_FALLBACK_false;
24+
2125
r_obj* out = vec_ptype_common(
2226
xs,
2327
ptype,
24-
S3_FALLBACK_false,
28+
finalise,
29+
s3_fallback,
2530
&xs_arg,
2631
call
2732
);
@@ -35,17 +40,22 @@ r_obj* ffi_ptype_common_params(r_obj* ffi_call, r_obj* op, r_obj* args, r_obj* e
3540

3641
r_obj* xs = r_node_car(args); args = r_node_cdr(args);
3742
r_obj* ptype = r_node_car(args); args = r_node_cdr(args);
43+
r_obj* ffi_finalise = r_node_car(args); args = r_node_cdr(args);
3844
r_obj* opts = r_node_car(args);
3945

4046
struct r_lazy call = { .x = syms.dot_call, .env = env };
4147
struct r_lazy xs_arg_lazy = { .x = syms.dot_arg, .env = env };
4248
struct vctrs_arg xs_arg = new_lazy_arg(&xs_arg_lazy);
4349

50+
const enum ptype_finalise finalise = r_arg_as_bool(ffi_finalise, "finalise") ?
51+
PTYPE_FINALISE_true :
52+
PTYPE_FINALISE_false;
4453
const enum s3_fallback s3_fallback = s3_fallback_from_opts(opts);
4554

4655
r_obj* out = vec_ptype_common(
4756
xs,
4857
ptype,
58+
finalise,
4959
s3_fallback,
5060
&xs_arg,
5161
call
@@ -54,39 +64,52 @@ r_obj* ffi_ptype_common_params(r_obj* ffi_call, r_obj* op, r_obj* args, r_obj* e
5464
return out;
5565
}
5666

67+
// Output is always finalised unless `PTYPE_FINALISE_false` is set, even if the
68+
// user supplies a `ptype`!
5769
r_obj* vec_ptype_common(
5870
r_obj* dots,
5971
r_obj* ptype,
72+
enum ptype_finalise finalise,
6073
enum s3_fallback s3_fallback,
6174
struct vctrs_arg* p_arg,
6275
struct r_lazy call
6376
) {
77+
int n_prot = 0;
78+
79+
r_obj* out;
80+
6481
if (!vec_is_partial(ptype)) {
65-
return vec_ptype(ptype, vec_args.dot_ptype, call);
82+
out = KEEP_N(vec_ptype(ptype, vec_args.dot_ptype, call), &n_prot);
83+
} else {
84+
if (r_is_true(r_peek_option("vctrs.no_guessing"))) {
85+
r_abort_lazy_call(r_lazy_null, "strict mode is activated; you must supply complete `.ptype`.");
86+
}
87+
88+
struct ptype_common_reduce_opts reduce_opts = {
89+
.call = call,
90+
.s3_fallback = s3_fallback
91+
};
92+
93+
// Start reduction with `ptype` in case it's a partial type
94+
out = KEEP_N(
95+
reduce(
96+
ptype,
97+
vec_args.dot_ptype,
98+
p_arg,
99+
dots,
100+
&ptype2_common,
101+
&reduce_opts
102+
),
103+
&n_prot
104+
);
66105
}
67106

68-
if (r_is_true(r_peek_option("vctrs.no_guessing"))) {
69-
r_abort_lazy_call(r_lazy_null, "strict mode is activated; you must supply complete `.ptype`.");
107+
if (should_finalise(finalise)) {
108+
out = KEEP_N(vec_ptype_finalise(out), &n_prot);
70109
}
71110

72-
struct ptype_common_reduce_opts reduce_opts = {
73-
.call = call,
74-
.s3_fallback = s3_fallback
75-
};
76-
77-
// Start reduction with the `.ptype` argument
78-
r_obj* type = KEEP(reduce(
79-
ptype,
80-
vec_args.dot_ptype,
81-
p_arg,
82-
dots,
83-
&ptype2_common,
84-
&reduce_opts
85-
));
86-
type = vec_ptype_finalise(type);
87-
88-
FREE(1);
89-
return type;
111+
FREE(n_prot);
112+
return out;
90113
}
91114

92115
static

src/ptype-common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define VCTRS_PTYPE_COMMON_H
33

44
#include "vctrs-core.h"
5+
#include "ptype.h"
56
#include "ptype2.h"
67
#include "utils.h"
78

@@ -13,6 +14,7 @@ bool vec_is_common_class_fallback(r_obj* ptype) {
1314
r_obj* vec_ptype_common(
1415
r_obj* dots,
1516
r_obj* ptype,
17+
enum ptype_finalise finalise,
1618
enum s3_fallback s3_fallback,
1719
struct vctrs_arg* p_arg,
1820
struct r_lazy call

src/ptype.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33

44
#include "vctrs-core.h"
55

6+
enum ptype_finalise {
7+
PTYPE_FINALISE_false,
8+
PTYPE_FINALISE_true
9+
};
10+
11+
#define PTYPE_FINALISE_DEFAULT PTYPE_FINALISE_true
12+
13+
static inline
14+
bool should_finalise(enum ptype_finalise finalise) {
15+
return finalise == PTYPE_FINALISE_true;
16+
}
17+
618
r_obj* vec_ptype(r_obj* x, struct vctrs_arg* x_arg, struct r_lazy call);
719
r_obj* vec_ptype_final(r_obj* x, struct vctrs_arg* x_arg, struct r_lazy call);
820

src/recode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ r_obj* ptype_finalize(
777777
ptype = KEEP(vec_ptype_common(
778778
to,
779779
r_null,
780+
PTYPE_FINALISE_DEFAULT,
780781
S3_FALLBACK_DEFAULT,
781782
p_to_arg,
782783
error_call

tests/testthat/_snaps/cast.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,11 @@
8383
i Please use `allow_lossy_cast()` instead.
8484
i We detected a lossy transformation from `x` <fct> to `to` <fct>. The result will contain lower-resolution values or missing values. To suppress this warning, wrap your code with `allow_lossy_cast()`.
8585

86+
# can cast to unspecified `NA` with `vec_cast()` and `vec_cast_common()` (#2099)
87+
88+
Code
89+
vec_cast(TRUE, to = unspecified(1))
90+
Condition
91+
Error:
92+
! Can't convert `TRUE` <logical> to <vctrs_unspecified>.
93+

0 commit comments

Comments
 (0)