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