1 #include "vctrs.h"
2 #include "ptype2.h"
3 #include "type-data-frame.h"
4 #include "utils.h"
5 #include "shape.h"
6 
7 static
8 SEXP vec_ptype2_switch_native(const struct ptype2_opts* opts,
9                               enum vctrs_type x_type,
10                               enum vctrs_type y_type,
11                               int* left);
12 
13 // [[ register() ]]
vctrs_ptype2_opts(SEXP x,SEXP y,SEXP opts,SEXP x_arg,SEXP y_arg)14 SEXP vctrs_ptype2_opts(SEXP x,
15                        SEXP y,
16                        SEXP opts,
17                        SEXP x_arg,
18                        SEXP y_arg) {
19   struct vctrs_arg c_x_arg = vec_as_arg(x_arg);
20   struct vctrs_arg c_y_arg = vec_as_arg(y_arg);
21 
22   const struct ptype2_opts c_opts = new_ptype2_opts(x, y, &c_x_arg, &c_y_arg, opts);
23 
24   int _left;
25   return vec_ptype2_opts(&c_opts, &_left);
26 }
27 
vec_ptype2_opts_impl(const struct ptype2_opts * opts,int * left,bool first_pass)28 SEXP vec_ptype2_opts_impl(const struct ptype2_opts* opts,
29                           int* left,
30                           bool first_pass) {
31   SEXP x = opts->x;
32   SEXP y = opts->y;
33   struct vctrs_arg* x_arg = opts->x_arg;
34   struct vctrs_arg* y_arg = opts->y_arg;
35 
36   enum vctrs_type x_type = vec_typeof(x);
37   enum vctrs_type y_type = vec_typeof(y);
38 
39   if (x_type == vctrs_type_null) {
40     *left = y == R_NilValue;
41     return vec_ptype2_from_unspecified(opts, x_type, y, y_arg);
42   }
43   if (y_type == vctrs_type_null) {
44     *left = x == R_NilValue;
45     return vec_ptype2_from_unspecified(opts, x_type, x, x_arg);
46   }
47 
48   if (x_type == vctrs_type_unspecified) {
49     return vec_ptype2_from_unspecified(opts, y_type, y, y_arg);
50   }
51   if (y_type == vctrs_type_unspecified) {
52     return vec_ptype2_from_unspecified(opts, x_type, x, x_arg);
53   }
54 
55   if (x_type == vctrs_type_scalar) {
56     stop_scalar_type(x, x_arg);
57   }
58   if (y_type == vctrs_type_scalar) {
59     stop_scalar_type(y, y_arg);
60   }
61 
62   if (x_type != vctrs_type_s3 && y_type != vctrs_type_s3) {
63     return vec_ptype2_switch_native(opts, x_type, y_type, left);
64   }
65 
66   if (x_type == vctrs_type_s3 || y_type == vctrs_type_s3) {
67     SEXP out = vec_ptype2_dispatch_native(opts, x_type, y_type, left);
68     if (out != R_NilValue) {
69       return out;
70     }
71   }
72 
73   // Try native dispatch again with prototypes, in case the prototype
74   // is another type. FIXME: Use R-level callback instead.
75   if (first_pass) {
76     struct ptype2_opts mut_opts = *opts;
77     mut_opts.x = PROTECT(vec_ptype(x, x_arg));
78     mut_opts.y = PROTECT(vec_ptype(y, y_arg));
79 
80     SEXP out = vec_ptype2_opts_impl(&mut_opts, left, false);
81 
82     UNPROTECT(2);
83     return out;
84   }
85 
86   return vec_ptype2_dispatch_s3(opts);
87 }
88 
89 // [[ include("ptype2.h") ]]
vec_ptype2_opts(const struct ptype2_opts * opts,int * left)90 SEXP vec_ptype2_opts(const struct ptype2_opts* opts,
91                      int* left) {
92   return vec_ptype2_opts_impl(opts, left, true);
93 }
94 
95 static
vec_ptype2_switch_native(const struct ptype2_opts * opts,enum vctrs_type x_type,enum vctrs_type y_type,int * left)96 SEXP vec_ptype2_switch_native(const struct ptype2_opts* opts,
97                               enum vctrs_type x_type,
98                               enum vctrs_type y_type,
99                               int* left) {
100   SEXP x = opts->x;
101   SEXP y = opts->y;
102   struct vctrs_arg* x_arg = opts->x_arg;
103   struct vctrs_arg* y_arg = opts->y_arg;
104 
105   enum vctrs_type2 type2 = vec_typeof2_impl(x_type, y_type, left);
106 
107   switch (type2) {
108   case vctrs_type2_null_null:
109     return R_NilValue;
110 
111   case vctrs_type2_logical_logical:
112     return vec_shaped_ptype(vctrs_shared_empty_lgl, x, y, x_arg, y_arg);
113 
114   case vctrs_type2_logical_integer:
115   case vctrs_type2_integer_integer:
116     return vec_shaped_ptype(vctrs_shared_empty_int, x, y, x_arg, y_arg);
117 
118   case vctrs_type2_logical_double:
119   case vctrs_type2_integer_double:
120   case vctrs_type2_double_double:
121     return vec_shaped_ptype(vctrs_shared_empty_dbl, x, y, x_arg, y_arg);
122 
123   case vctrs_type2_integer_complex:
124   case vctrs_type2_double_complex:
125   case vctrs_type2_complex_complex:
126     return vec_shaped_ptype(vctrs_shared_empty_cpl, x, y, x_arg, y_arg);
127 
128   case vctrs_type2_character_character:
129     return vec_shaped_ptype(vctrs_shared_empty_chr, x, y, x_arg, y_arg);
130 
131   case vctrs_type2_raw_raw:
132     return vec_shaped_ptype(vctrs_shared_empty_raw, x, y, x_arg, y_arg);
133 
134   case vctrs_type2_list_list:
135     return vec_shaped_ptype(vctrs_shared_empty_list, x, y, x_arg, y_arg);
136 
137   case vctrs_type2_dataframe_dataframe:
138     return df_ptype2(opts);
139 
140   default:
141     return vec_ptype2_dispatch_s3(opts);
142   }
143 }
144 
145 /**
146  * Return non-unspecified type.
147  *
148  * This is normally the `vec_ptype()` of the other input, but if the
149  * common class fallback is enabled we return the `vec_ptype2()` of
150  * this input with itself. This way we may return a fallback sentinel which can be
151  * treated specially, for instance in `vec_c(NA, x, NA)`.
152  */
vec_ptype2_from_unspecified(const struct ptype2_opts * opts,enum vctrs_type other_type,SEXP other,struct vctrs_arg * other_arg)153 SEXP vec_ptype2_from_unspecified(const struct ptype2_opts* opts,
154                                  enum vctrs_type other_type,
155                                  SEXP other,
156                                  struct vctrs_arg* other_arg) {
157   if (other_type == vctrs_type_unspecified || other_type == vctrs_type_null) {
158     return vec_ptype(other, other_arg);
159   }
160 
161   if (opts->fallback.s3) {
162     const struct ptype2_opts self_self_opts = (const struct ptype2_opts) {
163       .x = other,
164       .y = other,
165       .x_arg = other_arg,
166       .y_arg = other_arg,
167       .fallback = opts->fallback
168     };
169     int _left = 0;
170     return vec_ptype2_opts(&self_self_opts, &_left);
171   }
172 
173   return vec_ptype(other, other_arg);
174 }
175 
176 
177 struct is_coercible_data {
178   const struct ptype2_opts* opts;
179   int* dir;
180 };
181 
182 static
vec_is_coercible_cb(void * data_)183 void vec_is_coercible_cb(void* data_) {
184   struct is_coercible_data* data = (struct is_coercible_data*) data_;
185   vec_ptype2_opts(data->opts, data->dir);
186 }
187 
188 static
vec_is_coercible_e(const struct ptype2_opts * opts,int * dir,ERR * err)189 void vec_is_coercible_e(const struct ptype2_opts* opts,
190                         int* dir,
191                         ERR* err) {
192   struct is_coercible_data data = {
193     .opts = opts,
194     .dir = dir,
195   };
196 
197   *err = r_try_catch(&vec_is_coercible_cb,
198                      &data,
199                      syms_vctrs_error_incompatible_type,
200                      NULL,
201                      NULL);
202 }
203 
204 // [[ include("ptype2.h") ]]
vec_is_coercible(const struct ptype2_opts * opts,int * dir)205 bool vec_is_coercible(const struct ptype2_opts* opts,
206                       int* dir) {
207   ERR err = NULL;
208   vec_is_coercible_e(opts, dir, &err);
209   return !err;
210 }
211 
212 // [[ register() ]]
vctrs_is_coercible(SEXP x,SEXP y,SEXP opts,SEXP x_arg,SEXP y_arg)213 SEXP vctrs_is_coercible(SEXP x,
214                         SEXP y,
215                         SEXP opts,
216                         SEXP x_arg,
217                         SEXP y_arg) {
218   struct vctrs_arg c_x_arg = vec_as_arg(x_arg);
219   struct vctrs_arg c_y_arg = vec_as_arg(y_arg);
220 
221   const struct ptype2_opts c_opts = new_ptype2_opts(x, y, &c_x_arg, &c_y_arg, opts);
222 
223   int dir = 0;
224   return r_lgl(vec_is_coercible(&c_opts, &dir));
225 }
226 
227 
228 // [[ register() ]]
vctrs_ptype2(SEXP x,SEXP y,SEXP x_arg,SEXP y_arg)229 SEXP vctrs_ptype2(SEXP x, SEXP y, SEXP x_arg, SEXP y_arg) {
230   struct vctrs_arg x_arg_ = vec_as_arg(x_arg);
231   struct vctrs_arg y_arg_ = vec_as_arg(y_arg);
232 
233   int _left;
234   return vec_ptype2(x, y, &x_arg_, &y_arg_, &_left);
235 }
236 
237 // [[ include("ptype2.h") ]]
new_fallback_opts(SEXP opts)238 struct fallback_opts new_fallback_opts(SEXP opts) {
239   return (struct fallback_opts) {
240     .df = r_int_get(r_list_get(opts, 0), 0),
241     .s3 = r_int_get(r_list_get(opts, 1), 0)
242   };
243 }
244 
245 // [[ include("ptype2.h") ]]
new_ptype2_opts(SEXP x,SEXP y,struct vctrs_arg * x_arg,struct vctrs_arg * y_arg,SEXP opts)246 struct ptype2_opts new_ptype2_opts(SEXP x,
247                                    SEXP y,
248                                    struct vctrs_arg* x_arg,
249                                    struct vctrs_arg* y_arg,
250                                    SEXP opts) {
251   return (struct ptype2_opts) {
252     .x = x,
253     .y = y,
254     .x_arg = x_arg,
255     .y_arg = y_arg,
256     .fallback = new_fallback_opts(opts)
257   };
258 }
259 
260 static SEXP r_fallback_opts_template = NULL;
261 
262 // [[ include("ptype2.h") ]]
new_fallback_r_opts(const struct ptype2_opts * opts)263 SEXP new_fallback_r_opts(const struct ptype2_opts* opts) {
264   SEXP r_opts = PROTECT(r_copy(r_fallback_opts_template));
265 
266   r_int_poke(r_list_get(r_opts, 0), 0, opts->fallback.df);
267   r_int_poke(r_list_get(r_opts, 1), 0, opts->fallback.s3);
268 
269   UNPROTECT(1);
270   return r_opts;
271 }
272 
273 
vctrs_init_ptype2(SEXP ns)274 void vctrs_init_ptype2(SEXP ns) {
275   r_fallback_opts_template = r_parse_eval("fallback_opts()", ns);
276   R_PreserveObject(r_fallback_opts_template);
277 }
278