return 0;
 }
 
-
 static const struct core_reloc_test_case test_cases[] = {
        /* validate we can find kernel image and use its BTF for relocs */
        {
        SIZE_CASE(size___diff_offs),
        SIZE_ERR_CASE(size___err_ambiguous),
 
-       /* validate type existence and size relocations */
+       /* validate type existence, match, and size relocations */
        TYPE_BASED_CASE(type_based, {
                .struct_exists = 1,
                .union_exists = 1,
                .typedef_void_ptr_exists = 1,
                .typedef_func_proto_exists = 1,
                .typedef_arr_exists = 1,
+
+               .struct_matches = 1,
+               .union_matches = 1,
+               .enum_matches = 1,
+               .typedef_named_struct_matches = 1,
+               .typedef_anon_struct_matches = 1,
+               .typedef_struct_ptr_matches = 1,
+               .typedef_int_matches = 1,
+               .typedef_enum_matches = 1,
+               .typedef_void_ptr_matches = 1,
+               .typedef_func_proto_matches = 1,
+               .typedef_arr_matches = 1,
+
                .struct_sz = sizeof(struct a_struct),
                .union_sz = sizeof(union a_union),
                .enum_sz = sizeof(enum an_enum),
                .typedef_void_ptr_exists = 1,
                .typedef_func_proto_exists = 1,
                .typedef_arr_exists = 1,
+
+               .struct_matches = 0,
+               .union_matches = 0,
+               .enum_matches = 0,
+               .typedef_named_struct_matches = 0,
+               .typedef_anon_struct_matches = 0,
+               .typedef_struct_ptr_matches = 1,
+               .typedef_int_matches = 0,
+               .typedef_enum_matches = 0,
+               .typedef_void_ptr_matches = 1,
+               .typedef_func_proto_matches = 0,
+               .typedef_arr_matches = 0,
+
                .struct_sz = sizeof(struct a_struct___diff_sz),
                .union_sz = sizeof(union a_union___diff_sz),
                .enum_sz = sizeof(enum an_enum___diff_sz),
        }),
        TYPE_BASED_CASE(type_based___incompat, {
                .enum_exists = 1,
+               .enum_matches = 1,
                .enum_sz = sizeof(enum an_enum),
        }),
        TYPE_BASED_CASE(type_based___fn_wrong_args, {
                .struct_exists = 1,
+               .struct_matches = 1,
                .struct_sz = sizeof(struct a_struct),
        }),
 
 
 };
 
 /*
- * TYPE EXISTENCE & SIZE
+ * TYPE EXISTENCE, MATCH & SIZE
  */
 struct core_reloc_type_based_output {
        bool struct_exists;
        bool typedef_func_proto_exists;
        bool typedef_arr_exists;
 
+       bool struct_matches;
+       bool union_matches;
+       bool enum_matches;
+       bool typedef_named_struct_matches;
+       bool typedef_anon_struct_matches;
+       bool typedef_struct_ptr_matches;
+       bool typedef_int_matches;
+       bool typedef_enum_matches;
+       bool typedef_void_ptr_matches;
+       bool typedef_func_proto_matches;
+       bool typedef_arr_matches;
+
        int struct_sz;
        int union_sz;
        int enum_sz;
 
        bool typedef_func_proto_exists;
        bool typedef_arr_exists;
 
+       bool struct_matches;
+       bool union_matches;
+       bool enum_matches;
+       bool typedef_named_struct_matches;
+       bool typedef_anon_struct_matches;
+       bool typedef_struct_ptr_matches;
+       bool typedef_int_matches;
+       bool typedef_enum_matches;
+       bool typedef_void_ptr_matches;
+       bool typedef_func_proto_matches;
+       bool typedef_arr_matches;
+
        int struct_sz;
        int union_sz;
        int enum_sz;
 SEC("raw_tracepoint/sys_enter")
 int test_core_type_based(void *ctx)
 {
-#if __has_builtin(__builtin_preserve_type_info)
+       /* Support for the BPF_TYPE_MATCHES argument to the
+        * __builtin_preserve_type_info builtin was added at some point during
+        * development of clang 15 and it's what we require for this test. Part of it
+        * could run with merely __builtin_preserve_type_info (which could be checked
+        * separately), but we have to find an upper bound.
+        */
+#if __has_builtin(__builtin_preserve_type_info) && __clang_major__ >= 15
        struct core_reloc_type_based_output *out = (void *)&data.out;
 
        out->struct_exists = bpf_core_type_exists(struct a_struct);
        out->typedef_func_proto_exists = bpf_core_type_exists(func_proto_typedef);
        out->typedef_arr_exists = bpf_core_type_exists(arr_typedef);
 
+       out->struct_matches = bpf_core_type_matches(struct a_struct);
+       out->union_matches = bpf_core_type_matches(union a_union);
+       out->enum_matches = bpf_core_type_matches(enum an_enum);
+       out->typedef_named_struct_matches = bpf_core_type_matches(named_struct_typedef);
+       out->typedef_anon_struct_matches = bpf_core_type_matches(anon_struct_typedef);
+       out->typedef_struct_ptr_matches = bpf_core_type_matches(struct_ptr_typedef);
+       out->typedef_int_matches = bpf_core_type_matches(int_typedef);
+       out->typedef_enum_matches = bpf_core_type_matches(enum_typedef);
+       out->typedef_void_ptr_matches = bpf_core_type_matches(void_ptr_typedef);
+       out->typedef_func_proto_matches = bpf_core_type_matches(func_proto_typedef);
+       out->typedef_arr_matches = bpf_core_type_matches(arr_typedef);
+
        out->struct_sz = bpf_core_type_size(struct a_struct);
        out->union_sz = bpf_core_type_size(union a_union);
        out->enum_sz = bpf_core_type_size(enum an_enum);