#define UPDATE_CTX2 compile_copy_context(&unlang_ctx2, unlang_ctx)
 
 
-static unlang_t *compile_empty(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext);
+static unlang_t *compile_empty(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type);
 
 static char const unlang_spaces[] = "                                                                                                                                                                                                                                                                ";
 
 }
 
 
-static unlang_group_t *group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_ext_t const *ext)
+static unlang_group_t *group_allocate(unlang_t *parent, CONF_SECTION *cs, unlang_type_t type)
 {
        unlang_group_t  *g;
        unlang_t        *c;
        TALLOC_CTX      *ctx;
+       unlang_op_t const *op = &unlang_ops[type];
 
        ctx = parent;
        if (!ctx) ctx = cs;
 
+       fr_assert(op->unlang_size > 0);
+
        /*
         *      All the groups have a common header
         */
-       g = (unlang_group_t *)_talloc_zero_pooled_object(ctx, ext->len, ext->type_name,
-                                                        ext->pool_headers, ext->pool_len);
+       g = (unlang_group_t *)_talloc_zero_pooled_object(ctx, op->unlang_size, op->unlang_name,
+                                                        op->pool_headers, op->pool_len);
        if (!g) return NULL;
 
        g->children = NULL;
 
        c = unlang_group_to_generic(g);
        c->parent = parent;
-       c->type = ext->type;
+       c->type = type;
        c->ci = CF_TO_ITEM(cs);
 
        return g;
 
        tmpl_rules_t            t_rules;
 
-       static unlang_ext_t const map_ext = {
-               .type = UNLANG_TYPE_MAP,
-               .len = sizeof(unlang_map_t),
-               .type_name = "unlang_map_t"
-       };
-
        /*
         *      The RHS is NOT resolved in the context of the LHS.
         */
        }
        t_rules.literals_safe_for = map_proc_literals_safe_for(proc);
 
-       g = group_allocate(parent, cs, &map_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_MAP);
        if (!g) return NULL;
 
        gext = unlang_group_to_map(g);
 
        tmpl_rules_t            t_rules;
 
-       static unlang_ext_t const update_ext = {
-               .type = UNLANG_TYPE_UPDATE,
-               .len = sizeof(unlang_map_t),
-               .type_name = "unlang_map_t"
-       };
-
        if (main_config_migrate_option_get("forbid_update")) {
                cf_log_err(cs, "The use of 'update' sections is forbidden by the server configuration");
                return NULL;
        t_rules.attr.allow_wildcard = true;
        RULES_VERIFY(&t_rules);
 
-       g = group_allocate(parent, cs, &update_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_UPDATE);
        if (!g) return NULL;
 
        gext = unlang_group_to_map(g);
        return true;
 }
 
-static unlang_t *compile_empty(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_ext_t const *ext)
+static unlang_t *compile_empty(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
 {
        unlang_group_t *g;
        unlang_t *c;
         *      *interpreter* type is GROUP, even if the *debug names*
         *      are something else.
         */
-       g = group_allocate(parent, cs, ext);
+       g = group_allocate(parent, cs, type);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
        if (!cs) {
-               c->name = unlang_ops[ext->type].name;
+               c->name = unlang_ops[type].name;
                c->debug_name = c->name;
 
        } else {
 /*
  *     Generic "compile a section with more unlang inside of it".
  */
-static unlang_t *compile_section(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs,
-                                unlang_ext_t const *ext)
+static unlang_t *compile_section(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
 {
        unlang_group_t  *g;
        unlang_t        *c;
        /*
         *      We always create a group, even if the section is empty.
         */
-       g = group_allocate(parent, cs, ext);
+       g = group_allocate(parent, cs, type);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
         *      Make sure to tell the user that we're running a
         *      policy, and not anything else.
         */
-       if (ext->type == UNLANG_TYPE_POLICY) {
+       if (type == UNLANG_TYPE_POLICY) {
                MEM(c->debug_name = talloc_typed_asprintf(c, "policy %s", name1));
 
        } else if (!name2) {
 
 static unlang_t *compile_group(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
-       static unlang_ext_t const group = {
-               .type = UNLANG_TYPE_GROUP,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t",
-       };
-
        if (!cf_item_next(ci, NULL)) return UNLANG_IGNORE;
 
-       return compile_section(parent, unlang_ctx, cf_item_to_section(ci), &group);
+       return compile_section(parent, unlang_ctx, cf_item_to_section(ci), UNLANG_TYPE_GROUP);
 }
 
 static fr_table_num_sorted_t transaction_keywords[] = {
        unlang_t *c;
        unlang_compile_t unlang_ctx2;
 
-       static unlang_ext_t const transaction = {
-               .type = UNLANG_TYPE_TRANSACTION,
-               .len = sizeof(unlang_transaction_t),
-               .type_name = "unlang_transaction_t",
-       };
-
        if (cf_section_name2(cs) != NULL) {
                cf_log_err(cs, "Unexpected argument to 'transaction' section");
                cf_log_err(ci, DOC_KEYWORD_REF(transaction));
        unlang_ctx2.actions.actions[RLM_MODULE_INVALID] = MOD_ACTION_RETURN;
        unlang_ctx2.actions.actions[RLM_MODULE_DISALLOW] = MOD_ACTION_RETURN;
 
-       g = group_allocate(parent, cs, &transaction);
+       g = group_allocate(parent, cs, UNLANG_TYPE_TRANSACTION);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
        unlang_t *c;
        CONF_ITEM *next;
 
-       static unlang_ext_t const ext = {
-               .type = UNLANG_TYPE_TRY,
-               .len = sizeof(unlang_try_t),
-               .type_name = "unlang_try_t",
-       };
-
        /*
         *      The transaction is empty, ignore it.
         */
                goto print_url;
        }
 
-       g = group_allocate(parent, cs, &ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_TRY);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
        CONF_ITEM *prev;
        char const *name;
 
-       static unlang_ext_t const ext = {
-               .type = UNLANG_TYPE_CATCH,
-               .len = sizeof(unlang_catch_t),
-               .type_name = "unlang_catch_t",
-       };
-
        prev = cf_item_prev(cf_parent(ci), ci);
        while (prev && cf_item_is_data(prev)) prev = cf_item_prev(cf_parent(ci), prev);
 
                goto fail;
        }
 
-       g = group_allocate(parent, cs, &ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_CATCH);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
        fr_type_t               type;
        fr_htrie_type_t         htype;
 
-       static unlang_ext_t const switch_ext = {
-               .type = UNLANG_TYPE_SWITCH,
-               .len = sizeof(unlang_switch_t),
-               .type_name = "unlang_switch_t",
-               .pool_headers = TMPL_POOL_DEF_HEADERS,
-               .pool_len = TMPL_POOL_DEF_LEN
-       };
-
        /*
         *      We allow unknown attributes here.
         */
 
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
-       g = group_allocate(parent, cs, &switch_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_SWITCH);
        if (!g) return NULL;
 
        gext = unlang_group_to_switch(g);
        tmpl_t                  *vpt = NULL;
        tmpl_rules_t            t_rules;
 
-       static unlang_ext_t const case_ext = {
-               .type = UNLANG_TYPE_CASE,
-               .len = sizeof(unlang_case_t),
-               .type_name = "unlang_case_t",
-       };
-
        /*
         *      We allow unknown attributes here.
         */
         *      will silently skip the match, and then fall through to
         *      the "default" statement.
         */
-       c = compile_section(parent, unlang_ctx, cs, &case_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_CASE);
        if (!c) {
                talloc_free(vpt);
                return NULL;
        tmpl_t                  *vpt = NULL;
        fr_token_t              token;
 
-       static unlang_ext_t const timeout_ext = {
-               .type = UNLANG_TYPE_TIMEOUT,
-               .len = sizeof(unlang_timeout_t),
-               .type_name = "unlang_timeout_t",
-       };
-
        /*
         *      Timeout <time ref>
         */
 
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
-       g = group_allocate(parent, cs, &timeout_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_TIMEOUT);
        if (!g) return NULL;
 
        gext = unlang_group_to_timeout(g);
        /*
         *      Compile the contents of a "timeout".
         */
-       c = compile_section(parent, unlang_ctx, cs, &timeout_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_TIMEOUT);
        if (!c) return NULL;
 
        g = unlang_generic_to_group(c);
        ssize_t                 slen;
        tmpl_rules_t            t_rules;
 
-       static unlang_ext_t const limit_ext = {
-               .type = UNLANG_TYPE_LIMIT,
-               .len = sizeof(unlang_limit_t),
-               .type_name = "unlang_limit_t",
-       };
-
        /*
         *      limit <number>
         */
 
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
-       g = group_allocate(parent, cs, &limit_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_LIMIT);
        if (!g) return NULL;
 
        gext = unlang_group_to_limit(g);
        /*
         *      Compile the contents of a "limit".
         */
-       c = compile_section(parent, unlang_ctx, cs, &limit_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_LIMIT);
        if (!c) return NULL;
 
        g = unlang_generic_to_group(c);
        tmpl_rules_t            t_rules;
        unlang_compile_t        unlang_ctx2;
 
-       static unlang_ext_t const foreach_ext = {
-               .type = UNLANG_TYPE_FOREACH,
-               .len = sizeof(unlang_foreach_t),
-               .type_name = "unlang_foreach_t",
-               .pool_headers = TMPL_POOL_DEF_HEADERS,
-               .pool_len = TMPL_POOL_DEF_LEN
-       };
-
        /*
         *      Ignore empty "foreach" blocks, and don't even sanity check their arguments.
         */
        /*
         *      Allocate a group for the "foreach" block.
         */
-       g = group_allocate(parent, cs, &foreach_ext);
+       g = group_allocate(parent, cs, UNLANG_TYPE_FOREACH);
        if (!g) return NULL;
 
        c = unlang_group_to_generic(g);
 {
        unlang_t *unlang;
 
-       static unlang_ext_t const break_ext = {
-               .type = UNLANG_TYPE_BREAK,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t",
-       };
-
        for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
                /*
                 *      "break" doesn't go past a return point.
 
        parent->closed = true;
 
-       return compile_empty(parent, unlang_ctx, NULL, &break_ext);
+       return compile_empty(parent, unlang_ctx, NULL, UNLANG_TYPE_BREAK);
 }
 
 static unlang_t *compile_continue(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
        unlang_t *unlang;
 
-       static unlang_ext_t const break_ext = {
-               .type = UNLANG_TYPE_CONTINUE,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t",
-       };
-
        for (unlang = parent; unlang != NULL; unlang = unlang->parent) {
                /*
                 *      "continue" doesn't go past a return point.
 
        parent->closed = true;
 
-       return compile_empty(parent, unlang_ctx, NULL, &break_ext);
+       return compile_empty(parent, unlang_ctx, NULL, UNLANG_TYPE_CONTINUE);
 }
 
 
 {
        unlang_t *subrequest;
 
-       static unlang_ext_t const detach_ext = {
-               .type = UNLANG_TYPE_DETACH,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t",
-       };
-
        for (subrequest = parent;
             subrequest != NULL;
             subrequest = subrequest->parent) {
                return NULL;
        }
 
-       return compile_empty(parent, unlang_ctx, NULL, &detach_ext);
+       return compile_empty(parent, unlang_ctx, NULL, UNLANG_TYPE_DETACH);
 }
 
 static unlang_t *compile_return(unlang_t *parent, unlang_compile_t *unlang_ctx, UNUSED CONF_ITEM const *ci)
 {
-       static unlang_ext_t const return_ext = {
-               .type = UNLANG_TYPE_RETURN,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t",
-       };
-
        /*
         *      These types are all parallel, and therefore can have a "return" in them.
         */
                break;
        }
 
-       return compile_empty(parent, unlang_ctx, NULL, &return_ext);
+       return compile_empty(parent, unlang_ctx, NULL, UNLANG_TYPE_RETURN);
 }
 
 static unlang_t *compile_tmpl(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci)
        L("{"),
 );
 
-static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs,
-                                      unlang_ext_t const *ext)
+static unlang_t *compile_if_subsection(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs, unlang_type_t type)
 {
        unlang_t                *c;
 
        };
 
        if (!cf_section_name2(cs)) {
-               cf_log_err(cs, "'%s' without condition", unlang_ops[ext->type].name);
+               cf_log_err(cs, "'%s' without condition", unlang_ops[type].name);
                return NULL;
        }
 
                 */
                if (is_truthy && !value) {
                        cf_log_debug_prefix(cs, "Skipping contents of '%s' as it is always 'false'",
-                                           unlang_ops[ext->type].name);
+                                           unlang_ops[type].name);
 
                        /*
                         *      Free the children, which frees any xlats,
                         *
                         *      However, we still need to cache the conditions, as they will be accessed at run-time.
                         */
-                       c = compile_empty(parent, unlang_ctx, cs, ext);
+                       c = compile_empty(parent, unlang_ctx, cs, type);
                        cf_section_free_children(cs);
                } else {
-                       c = compile_section(parent, unlang_ctx, cs, ext);
+                       c = compile_section(parent, unlang_ctx, cs, type);
                }
        }
 
 
 static unlang_t *compile_if(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
-       static unlang_ext_t const if_ext = {
-               .type = UNLANG_TYPE_IF,
-               .len = sizeof(unlang_cond_t),
-               .type_name = "unlang_cond_t",
-               .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
-               .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2)
-       };
-
-       return compile_if_subsection(parent, unlang_ctx, cf_item_to_section(ci), &if_ext);
+       return compile_if_subsection(parent, unlang_ctx, cf_item_to_section(ci), UNLANG_TYPE_IF);
 }
 
 static unlang_t *compile_elsif(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
-       static unlang_ext_t const elsif_ext = {
-               .type = UNLANG_TYPE_ELSIF,
-               .len = sizeof(unlang_cond_t),
-               .type_name = "unlang_cond_t",
-               .pool_headers = 1 + 1 + (TMPL_POOL_DEF_HEADERS * 2),
-               .pool_len = sizeof(map_t) + (TMPL_POOL_DEF_LEN * 2)
-       };
-
-       return compile_if_subsection(parent, unlang_ctx, cf_item_to_section(ci), &elsif_ext);
+       return compile_if_subsection(parent, unlang_ctx, cf_item_to_section(ci), UNLANG_TYPE_ELSIF);
 }
 
 static unlang_t *compile_else(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
        CONF_SECTION *cs = cf_item_to_section(ci);
 
-       static unlang_ext_t const else_ext = {
-               .type = UNLANG_TYPE_ELSE,
-               .len = sizeof(unlang_group_t),
-               .type_name = "unlang_group_t"
-       };
-
        if (cf_section_name2(cs)) {
                cf_log_err(cs, "'else' cannot have a condition");
                return NULL;
        }
 
-       return compile_section(parent, unlang_ctx, cs, &else_ext);
+       return compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_ELSE);
 }
 
 /*
        CONF_SECTION                    *cs = cf_item_to_section(ci);
        unlang_t                        *c;
 
-       static unlang_ext_t const       redundant_ext = {
-                                               .type = UNLANG_TYPE_REDUNDANT,
-                                               .len = sizeof(unlang_group_t),
-                                               .type_name = "unlang_group_t"
-                                       };
-
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
        if (!validate_limited_subsection(cs, cf_section_name1(cs))) {
                return NULL;
        }
 
-       c = compile_section(parent, unlang_ctx, cs, &redundant_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_REDUNDANT);
        if (!c) return NULL;
 
        /*
 }
 
 static unlang_t *compile_load_balance_subsection(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs,
-                                                unlang_ext_t const *ext)
+                                                unlang_type_t type)
 {
        char const                      *name2;
        unlang_t                        *c;
         *      No children?  Die!
         */
        if (!cf_item_next(cs, NULL)) {
-               cf_log_err(cs, "%s sections cannot be empty", unlang_ops[ext->type].name);
+               cf_log_err(cs, "%s sections cannot be empty", unlang_ops[type].name);
                return NULL;
        }
 
        if (!validate_limited_subsection(cs, cf_section_name1(cs))) return NULL;
 
-       c = compile_section(parent, unlang_ctx, cs, ext);
+       c = compile_section(parent, unlang_ctx, cs, type);
        if (!c) return NULL;
 
        g = unlang_generic_to_group(c);
        }
 
        if (name2) {
-               fr_token_t type;
+               fr_token_t quote;
                ssize_t slen;
 
                /*
                 *      Create the template.  All attributes and xlats are
                 *      defined by now.
                 */
-               type = cf_section_name2_quote(cs);
+               quote = cf_section_name2_quote(cs);
                gext = unlang_group_to_load_balance(g);
                slen = tmpl_afrom_substr(gext, &gext->vpt,
                                         &FR_SBUFF_IN(name2, strlen(name2)),
-                                        type,
+                                        quote,
                                         NULL,
                                         &t_rules);
                if (!gext->vpt) {
 
 static unlang_t *compile_load_balance(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
-       static unlang_ext_t const load_balance_ext = {
-               .type = UNLANG_TYPE_LOAD_BALANCE,
-               .len = sizeof(unlang_load_balance_t),
-               .type_name = "unlang_load_balance_t"
-       };
-
-       return compile_load_balance_subsection(parent, unlang_ctx, cf_item_to_section(ci), &load_balance_ext);
+       return compile_load_balance_subsection(parent, unlang_ctx, cf_item_to_section(ci), UNLANG_TYPE_LOAD_BALANCE);
 }
 
 
 static unlang_t *compile_redundant_load_balance(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
 {
-       static unlang_ext_t const redundant_load_balance_ext = {
-               .type = UNLANG_TYPE_REDUNDANT_LOAD_BALANCE,
-               .len = sizeof(unlang_load_balance_t),
-               .type_name = "unlang_load_balance_t"
-       };
-
-       return compile_load_balance_subsection(parent, unlang_ctx, cf_item_to_section(ci), &redundant_load_balance_ext);
+       return compile_load_balance_subsection(parent, unlang_ctx, cf_item_to_section(ci), UNLANG_TYPE_REDUNDANT_LOAD_BALANCE);
 }
 
 static unlang_t *compile_parallel(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci)
        bool                            clone = true;
        bool                            detach = false;
 
-       static unlang_ext_t const       parallel_ext = {
-                                               .type = UNLANG_TYPE_PARALLEL,
-                                               .len = sizeof(unlang_parallel_t),
-                                               .type_name = "unlang_parallel_t"
-                                       };
-
        if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
 
        /*
                return NULL;
        }
 
-       c = compile_section(parent, unlang_ctx, cs, ¶llel_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_PARALLEL);
        if (!c) return NULL;
 
        g = unlang_generic_to_group(c);
 
        tmpl_t                          *vpt = NULL, *src_vpt = NULL, *dst_vpt = NULL;
 
-       static unlang_ext_t const       subrequest_ext = {
-                                               .type = UNLANG_TYPE_SUBREQUEST,
-                                               .len = sizeof(unlang_subrequest_t),
-                                               .type_name = "unlang_subrequest_t",
-                                               .pool_headers = (TMPL_POOL_DEF_HEADERS * 3),
-                                               .pool_len = (TMPL_POOL_DEF_LEN * 3)
-                                       };
-
        /*
         *      subrequest { ... }
         *
        /*
         *      Compile the subsection with a *different* default dictionary.
         */
-       c = compile_section(parent, &unlang_ctx2, cs, &subrequest_ext);
+       c = compile_section(parent, &unlang_ctx2, cs, UNLANG_TYPE_SUBREQUEST);
        if (!c) return NULL;
 
        /*
        fr_dict_t const                 *dict;
        fr_dict_attr_t const            *attr_packet_type;
 
-       static unlang_ext_t const       call_ext = {
-                                               .type = UNLANG_TYPE_CALL,
-                                               .len = sizeof(unlang_call_t),
-                                               .type_name = "unlang_call_t",
-                                       };
-
        server = cf_section_name2(cs);
        if (!server) {
                cf_log_err(cs, "You MUST specify a server name for 'call <server> { ... }'");
                return NULL;
        }
 
-       c = compile_section(parent, unlang_ctx, cs, &call_ext);
+       c = compile_section(parent, unlang_ctx, cs, UNLANG_TYPE_CALL);
        if (!c) return NULL;
 
        /*
 
        fr_dict_autoload_talloc_t       *dict_ref = NULL;
 
-       static unlang_ext_t const       caller_ext = {
-                                               .type = UNLANG_TYPE_CALLER,
-                                               .len = sizeof(unlang_caller_t),
-                                               .type_name = "unlang_caller_t",
-                                       };
-
        name = cf_section_name2(cs);
        if (!name) {
                cf_log_err(cs, "You MUST specify a protocol name for 'caller <protocol> { ... }'");
        unlang_ctx2.section_name1 = "caller";
        unlang_ctx2.section_name2 = name;
 
-       c = compile_section(parent, &unlang_ctx2, cs, &caller_ext);
+       c = compile_section(parent, &unlang_ctx2, cs, UNLANG_TYPE_CALLER);
        if (!c) {
                talloc_free(dict_ref);
                return NULL;
        unlang_compile_t                unlang_ctx2;
        unlang_t                        *c;
 
-       static unlang_ext_t const       policy_ext = {
-                                               .type = UNLANG_TYPE_POLICY,
-                                               .len = sizeof(unlang_group_t),
-                                               .type_name = "unlang_group_t",
-                                       };
-
-       static unlang_ext_t const       group_ext = {
-                                               .type = UNLANG_TYPE_GROUP,
-                                               .len = sizeof(unlang_group_t),
-                                               .type_name = "unlang_group_t",
-                                       };
-
        /*
         *      module.c takes care of ensuring that this is:
         *
                 *      group foo { ...
                 */
                c = compile_section(parent, &unlang_ctx2, subcs,
-                                   policy ? &policy_ext : &group_ext);
+                                   policy ? UNLANG_TYPE_POLICY : UNLANG_TYPE_GROUP);
        }
        if (!c) return NULL;
        fr_assert(c != UNLANG_IGNORE);
        tmpl_rules_t                    my_rules;
        char const                      *name1, *name2;
        CONF_DATA const                 *cd;
-       static unlang_ext_t const       group_ext = {
-                                               .type = UNLANG_TYPE_GROUP,
-                                               .len = sizeof(unlang_group_t),
-                                               .type_name = "unlang_group_t",
-       };
 
        /*
         *      Don't compile it twice, and don't print out debug
                                .actions = *actions,
                                .rules = rules
                            },
-                           cs, &group_ext);
+                           cs, UNLANG_TYPE_GROUP);
        if (!c) return -1;
 
        if (DEBUG_ENABLED4) unlang_dump(c, 2);