1 #include "vctrs.h"
2 #include "c.h"
3 #include "ptype-common.h"
4 #include "slice-assign.h"
5 #include "owned.h"
6 #include "utils.h"
7
8
9 // [[ register(external = TRUE) ]]
vctrs_c(SEXP call,SEXP op,SEXP args,SEXP env)10 SEXP vctrs_c(SEXP call, SEXP op, SEXP args, SEXP env) {
11 args = CDR(args);
12
13 SEXP xs = PROTECT(rlang_env_dots_list(env));
14 SEXP ptype = PROTECT(Rf_eval(CAR(args), env)); args = CDR(args);
15 SEXP name_spec = PROTECT(Rf_eval(CAR(args), env)); args = CDR(args);
16 SEXP name_repair = PROTECT(Rf_eval(CAR(args), env));
17
18 struct name_repair_opts name_repair_opts = new_name_repair_opts(name_repair, args_empty, false);
19 PROTECT_NAME_REPAIR_OPTS(&name_repair_opts);
20
21 SEXP out = vec_c(xs, ptype, name_spec, &name_repair_opts);
22
23 UNPROTECT(5);
24 return out;
25 }
26
27
28 // [[ include("vctrs.h") ]]
vec_c(SEXP xs,SEXP ptype,SEXP name_spec,const struct name_repair_opts * name_repair)29 SEXP vec_c(SEXP xs,
30 SEXP ptype,
31 SEXP name_spec,
32 const struct name_repair_opts* name_repair) {
33 struct fallback_opts opts = {
34 .df = DF_FALLBACK_DEFAULT,
35 .s3 = S3_FALLBACK_true
36 };
37 return vec_c_opts(xs, ptype, name_spec, name_repair, &opts);
38 }
39
vec_c_opts(SEXP xs,SEXP ptype,SEXP name_spec,const struct name_repair_opts * name_repair,const struct fallback_opts * fallback_opts)40 SEXP vec_c_opts(SEXP xs,
41 SEXP ptype,
42 SEXP name_spec,
43 const struct name_repair_opts* name_repair,
44 const struct fallback_opts* fallback_opts) {
45 SEXP orig_ptype = ptype;
46 ptype = PROTECT(vec_ptype_common_opts(xs, orig_ptype, fallback_opts));
47
48 if (ptype == R_NilValue) {
49 UNPROTECT(1);
50 return R_NilValue;
51 }
52
53 if (needs_vec_c_fallback(ptype)) {
54 SEXP out = vec_c_fallback(ptype, xs, name_spec, name_repair);
55 UNPROTECT(1);
56 return out;
57 }
58 // FIXME: Needed for dplyr::summarise() which passes a non-fallback ptype
59 if (needs_vec_c_homogeneous_fallback(xs, ptype)) {
60 SEXP out = vec_c_fallback_invoke(xs, name_spec);
61 UNPROTECT(1);
62 return out;
63 }
64
65 // FIXME: If data frame, recompute ptype without common class
66 // fallback. Should refactor this to allow common class fallback
67 // with data frame columns.
68 //
69 // FIXME: If `ptype` is a `vctrs_vctr` class without a
70 // `vec_ptype2()` method, the common type is a common class
71 // fallback. To avoid infinit recursion through `c.vctrs_vctr()`, we
72 // bail out from `needs_vec_c_fallback()`. In this case recurse with
73 // fallback disabled as well.
74 if ((is_data_frame(ptype) && fallback_opts->s3 == S3_FALLBACK_true) ||
75 vec_is_common_class_fallback(ptype)) {
76 struct fallback_opts d_fallback_opts = *fallback_opts;
77 d_fallback_opts.s3 = S3_FALLBACK_false;
78 ptype = PROTECT(vec_ptype_common_opts(xs, orig_ptype, &d_fallback_opts));
79 } else {
80 ptype = PROTECT(vec_ptype_common_opts(xs, ptype, fallback_opts));
81 }
82
83 // Find individual input sizes and total size of output
84 R_len_t n = Rf_length(xs);
85 R_len_t out_size = 0;
86
87 // Caching the sizes causes an extra allocation but it improves performance
88 SEXP sizes = PROTECT(Rf_allocVector(INTSXP, n));
89 int* p_sizes = INTEGER(sizes);
90
91 for (R_len_t i = 0; i < n; ++i) {
92 SEXP x = VECTOR_ELT(xs, i);
93 R_len_t size = (x == R_NilValue) ? 0 : vec_size(x);
94 out_size += size;
95 p_sizes[i] = size;
96 }
97
98 SEXP out = vec_init(ptype, out_size);
99 PROTECT_INDEX out_pi;
100 PROTECT_WITH_INDEX(out, &out_pi);
101
102 out = vec_proxy(out);
103 REPROTECT(out, out_pi);
104
105 SEXP loc = PROTECT(compact_seq(0, 0, true));
106 int* p_loc = INTEGER(loc);
107
108 bool assign_names = !Rf_inherits(name_spec, "rlang_zap");
109 SEXP xs_names = PROTECT(r_names(xs));
110 bool xs_is_named = xs_names != R_NilValue && !is_data_frame(ptype);
111
112 SEXP out_names = R_NilValue;
113 PROTECT_INDEX out_names_pi;
114 PROTECT_WITH_INDEX(R_NilValue, &out_names_pi);
115
116 // Compact sequences use 0-based counters
117 R_len_t counter = 0;
118
119 const struct vec_assign_opts c_assign_opts = {
120 .assign_names = assign_names,
121 .ignore_outer_names = true
122 };
123
124 for (R_len_t i = 0; i < n; ++i) {
125 SEXP x = VECTOR_ELT(xs, i);
126 R_len_t size = p_sizes[i];
127
128 init_compact_seq(p_loc, counter, size, true);
129
130 if (assign_names) {
131 SEXP outer = xs_is_named ? STRING_ELT(xs_names, i) : R_NilValue;
132 SEXP inner = PROTECT(vec_names(x));
133 SEXP x_nms = PROTECT(apply_name_spec(name_spec, outer, inner, size));
134
135 if (x_nms != R_NilValue) {
136 R_LAZY_ALLOC(out_names, out_names_pi, STRSXP, out_size);
137
138 // If there is no name to assign, skip the assignment since
139 // `out_names` already contains empty strings
140 if (x_nms != chrs_empty) {
141 out_names = chr_assign(out_names, loc, x_nms, VCTRS_OWNED_true);
142 REPROTECT(out_names, out_names_pi);
143 }
144 }
145
146 UNPROTECT(2);
147 }
148
149 if (!size) {
150 continue;
151 }
152
153 struct cast_opts opts = (struct cast_opts) {
154 .x = x,
155 .to = ptype,
156 .fallback = *fallback_opts
157 };
158 x = PROTECT(vec_cast_opts(&opts));
159
160 // Total ownership of `out` because it was freshly created with `vec_init()`
161 out = vec_proxy_assign_opts(out, loc, x, VCTRS_OWNED_true, &c_assign_opts);
162 REPROTECT(out, out_pi);
163
164 counter += size;
165 UNPROTECT(1);
166 }
167
168 out = PROTECT(vec_restore(out, ptype, R_NilValue, VCTRS_OWNED_true));
169
170 if (out_names != R_NilValue) {
171 out_names = PROTECT(vec_as_names(out_names, name_repair));
172 out = vec_set_names(out, out_names);
173 UNPROTECT(1);
174 } else if (!assign_names) {
175 // FIXME: `vec_ptype2()` doesn't consistently zaps names, so `out`
176 // might have been initialised with names. This branch can be
177 // removed once #1020 is resolved.
178 out = vec_set_names(out, R_NilValue);
179 }
180
181 UNPROTECT(8);
182 return out;
183 }
184
185 static inline bool vec_implements_base_c(SEXP x);
186
187 // [[ include("c.h") ]]
needs_vec_c_fallback(SEXP ptype)188 bool needs_vec_c_fallback(SEXP ptype) {
189 if (!vec_is_common_class_fallback(ptype)) {
190 return false;
191 }
192
193 // Suboptimal: Prevent infinite recursion through `vctrs_vctr` method
194 SEXP class = PROTECT(Rf_getAttrib(ptype, syms_fallback_class));
195 class = r_chr_get(class, r_length(class) - 1);
196
197 if (class == strings_vctrs_vctr) {
198 UNPROTECT(1);
199 return false;
200 }
201
202 UNPROTECT(1);
203 return true;
204 }
205
206 // [[ include("c.h") ]]
needs_vec_c_homogeneous_fallback(SEXP xs,SEXP ptype)207 bool needs_vec_c_homogeneous_fallback(SEXP xs, SEXP ptype) {
208 if (!Rf_length(xs)) {
209 return false;
210 }
211
212 SEXP x = list_first_non_null(xs, NULL);
213 if (!vec_is_vector(x)) {
214 return false;
215 }
216
217 // Never fall back for `vctrs_vctr` classes to avoid infinite
218 // recursion through `c.vctrs_vctr()`
219 if (Rf_inherits(x, "vctrs_vctr")) {
220 return false;
221 }
222
223 if (ptype != R_NilValue) {
224 SEXP x_class = PROTECT(r_class(x));
225 SEXP ptype_class = PROTECT(r_class(ptype));
226 bool equal = equal_object(x_class, ptype_class);
227 UNPROTECT(2);
228
229 if (!equal) {
230 return false;
231 }
232 }
233
234 return
235 !vec_implements_ptype2(x) &&
236 list_is_homogeneously_classed(xs) &&
237 vec_implements_base_c(x);
238 }
239
240 static inline
vec_implements_base_c(SEXP x)241 bool vec_implements_base_c(SEXP x) {
242 if (!OBJECT(x)) {
243 return false;
244 }
245
246 if (IS_S4_OBJECT(x)) {
247 return s4_find_method(x, s4_c_method_table) != R_NilValue;
248 } else {
249 return s3_find_method("c", x, base_method_table) != R_NilValue;
250 }
251 }
252 static inline
class_implements_base_c(SEXP cls)253 bool class_implements_base_c(SEXP cls) {
254 if (s3_class_find_method("c", cls, base_method_table) != R_NilValue) {
255 return true;
256 }
257 if (s4_class_find_method(cls, s4_c_method_table) != R_NilValue) {
258 return true;
259 }
260 return false;
261 }
262
263 static inline int vec_c_fallback_validate_args(SEXP x, SEXP name_spec);
264 static inline void stop_vec_c_fallback(SEXP xs, int err_type);
265
266 // [[ include("c.h") ]]
vec_c_fallback(SEXP ptype,SEXP xs,SEXP name_spec,const struct name_repair_opts * name_repair)267 SEXP vec_c_fallback(SEXP ptype,
268 SEXP xs,
269 SEXP name_spec,
270 const struct name_repair_opts* name_repair) {
271 SEXP class = PROTECT(Rf_getAttrib(ptype, syms_fallback_class));
272 bool implements_c = class_implements_base_c(class);
273 UNPROTECT(1);
274
275 if (implements_c) {
276 return vec_c_fallback_invoke(xs, name_spec);
277 } else {
278 struct fallback_opts fallback_opts = {
279 .df = DF_FALLBACK_none,
280 .s3 = S3_FALLBACK_false
281 };
282
283 // Should cause a common type error, unless another fallback
284 // kicks in (for instance, homogeneous class with homogeneous
285 // attributes)
286 vec_ptype_common_opts(xs, R_NilValue, &fallback_opts);
287
288 // Suboptimal: Call `vec_c()` again to combine vector with
289 // homogeneous class fallback
290 return vec_c_opts(xs, R_NilValue, name_spec, name_repair, &fallback_opts);
291 }
292 }
293
294 // [[ include("c.h") ]]
vec_c_fallback_invoke(SEXP xs,SEXP name_spec)295 SEXP vec_c_fallback_invoke(SEXP xs, SEXP name_spec) {
296 SEXP x = list_first_non_null(xs, NULL);
297
298 if (vctrs_debug_verbose) {
299 Rprintf("Falling back to `base::c()` for class `%s`.\n",
300 r_chr_get_c_string(r_class(x), 0));
301 }
302
303 int err_type = vec_c_fallback_validate_args(x, name_spec);
304 if (err_type) {
305 stop_vec_c_fallback(xs, err_type);
306 }
307
308 SEXP call = PROTECT(Rf_lang2(Rf_install("base_c_invoke"), xs));
309 SEXP out = Rf_eval(call, vctrs_ns_env);
310
311 UNPROTECT(1);
312 return out;
313 }
314
315 static inline
vec_c_fallback_validate_args(SEXP x,SEXP name_spec)316 int vec_c_fallback_validate_args(SEXP x, SEXP name_spec) {
317 if (name_spec != R_NilValue) {
318 return 2;
319 }
320 return 0;
321 }
322
stop_vec_c_fallback(SEXP xs,int err_type)323 static void stop_vec_c_fallback(SEXP xs, int err_type) {
324 SEXP common_class = PROTECT(r_class(list_first_non_null(xs, NULL)));
325 const char* class_str = r_chr_get_c_string(common_class, 0);
326
327 const char* msg = NULL;
328 switch (err_type) {
329 case 2: msg = "Can't use a name specification with non-vctrs types."; break;
330 case 3: msg = "Can't find vctrs or base methods for concatenation."; break;
331 default: msg = "Internal error: Unexpected error type."; break;
332 }
333
334 Rf_errorcall(R_NilValue,
335 "%s\n"
336 "vctrs methods must be implemented for class `%s`.\n"
337 "See <https://vctrs.r-lib.org/articles/s3-vector.html>.",
338 msg,
339 class_str);
340 }
341