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