+void psi_set_func_free(struct psi_set_func **func_ptr)
+{
+ if (*func_ptr) {
+ struct psi_set_func *func = *func_ptr;
+
+ *func_ptr = NULL;
+ psi_token_free(&func->token);
+ psi_decl_var_free(&func->var);
+ zend_string_release(func->name);
+ free(func);
+ }
+}
+
+void psi_set_func_dump(struct psi_dump *dump, struct psi_set_func *func, unsigned level)
+{
+ PSI_DUMP(dump, "%s(", func->name->val);
+ psi_decl_var_dump(dump, func->var);
+ PSI_DUMP(dump, "\t/* fqn=%s */", func->var->fqn->val);
+ if (func->inner && !func->recursive) {
+ size_t i = 0, count = psi_plist_count(func->inner);
+ struct psi_set_exp *inner;
+
+ PSI_DUMP(dump, ",");
+ ++level;
+ while (psi_plist_get(func->inner, i++, &inner)) {
+ PSI_DUMP(dump, "\n");
+ psi_set_exp_dump(dump, inner, level, i == count);
+ }
+ --level;
+ }
+ if (func->recursive) {
+ PSI_DUMP(dump, ", ...");
+ }
+ if (func->inner && !func->recursive) {
+ PSI_DUMP(dump, "\n%s", psi_t_indent(level));
+ }
+ PSI_DUMP(dump, ")");
+}
+
+static inline bool psi_set_func_validate_to_string(struct psi_data *data,
+ struct psi_set_func *func, struct psi_set_exp *set,
+ struct psi_impl *impl)
+{
+ struct psi_set_exp *sub_exp;
+
+ psi_plist_get(func->inner, 0, &sub_exp);
+
+ switch (psi_plist_count(func->inner)) {
+ default:
+ data->error(data, **(struct psi_token ***) &sub_exp->data, PSI_WARNING,
+ "Unexpected sub-expressions");
+ return false;
+
+ case 1:
+ if (sub_exp->kind != PSI_SET_NUMEXP) {
+ data->error(data, **(struct psi_token ***) &sub_exp->data,
+ PSI_WARNING, "Expected numeric expression");
+ return false;
+ }
+ func->handler = psi_set_to_stringl;
+ return true;
+
+ case 0:
+ func->handler = psi_set_to_string;
+ return true;
+ }
+}
+
+static inline bool psi_set_func_validate_to_array(struct psi_data *data,
+ struct psi_set_func *func, struct psi_set_exp *set,
+ struct psi_impl *impl)
+{
+ struct psi_set_exp *sub_exp;
+ struct psi_decl_var *set_var = psi_set_exp_get_decl_var(set);
+
+ switch (psi_plist_count(set->inner)) {
+ case 0:
+ if (func->recursive) {
+ func->handler = psi_set_to_recursive;
+ set->inner = func->inner = set->outer->inner;
+ return true;
+ }
+ data->error(data, func->token, PSI_WARNING,
+ "Expected sub-expressions in to_array() cast expression");
+ return false;
+
+ case 1:
+ psi_plist_get(set->inner, 0, &sub_exp);
+ if (sub_exp->kind != PSI_SET_FUNC) {
+ data->error(data, **(struct psi_token ***) &sub_exp->data,
+ PSI_WARNING, "Expected to_type() cast expression");
+ return false;
+ }
+ if (!zend_string_equals(set_var->name, psi_set_exp_get_decl_var(sub_exp)->name)) {
+ /* no warning, because of ambiguity with a one-field-struct monstrosity */
+ goto complex;
+ }
+ func->handler = psi_set_to_array_simple;
+ return true;
+
+ case 2:
+ psi_plist_get(set->inner, 0, &sub_exp);
+ if (sub_exp->kind != PSI_SET_NUMEXP) {
+ goto complex;
+ }
+ psi_plist_get(set->inner, 1, &sub_exp);
+ if (sub_exp->kind != PSI_SET_FUNC) {
+ data->error(data, **(struct psi_token ***) &sub_exp->data,
+ PSI_WARNING, "Expected to_type() cast expression");
+ return false;
+ }
+ if (!zend_string_equals(set_var->name, psi_set_exp_get_decl_var(sub_exp)->name)) {
+ data->error(data, **(struct psi_token ***) &sub_exp->data,
+ PSI_WARNING,
+ "Expected %s(%s) cast expression to reference the same"
+ " variable like the outer `set` statement, '%s'",
+ sub_exp->data.func->name->val,
+ psi_set_exp_get_decl_var(sub_exp)->name->val,
+ set_var->name->val);
+ return false;
+ }
+ func->handler = psi_set_to_array_counted;
+ return true;
+
+ default:
+ complex:
+ switch (psi_decl_type_get_real(set_var->arg->type)->type) {
+ case PSI_T_UNION:
+ case PSI_T_STRUCT:
+ break;
+ default:
+ data->error(data, func->token, PSI_WARNING,
+ "Expected struct or union type for complex to_array()"
+ " cast expression, got '%s'",
+ set_var->arg->type->name->val);
+ return false;
+ }
+ func->handler = psi_set_to_array;
+ return true;
+ }
+}
+
+static inline bool psi_set_func_validate_to_recursive(struct psi_data *data,
+ struct psi_set_func *func, struct psi_set_exp *set,
+ struct psi_impl *impl)
+{
+ if (!set->outer
+ || set->outer->kind != PSI_SET_FUNC
+ || set->outer->data.func->type != PSI_T_TO_ARRAY) {
+ data->error(data, func->token, PSI_WARNING,
+ "Expected to_array() as parent to recursion in `set` statement"
+ " of implementation '%s'",
+ impl->func->name->val);
+ return false;