diff --git a/src/Type.cc b/src/Type.cc index 05bc7a1faf..9db32d6ae9 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "zeek/Attr.h" #include "zeek/Desc.h" @@ -469,11 +470,19 @@ detail::TraversalCode IndexType::Traverse(detail::TraversalCallback* cb) const HANDLE_TC_TYPE_POST(tc); } -static bool is_supported_index_type(const TypePtr& t, const char** tname) +static bool is_supported_index_type(const TypePtr& t, const char** tname, + std::unordered_set& seen) { if ( t->InternalType() != TYPE_INTERNAL_OTHER ) return true; + // Handle recursive calls as good: If they turn out not + // to be, that should've been discovered further down. + if ( seen.count(t) > 0 ) + return true; + + seen.insert(t); + auto tag = t->Tag(); switch ( tag ) @@ -490,7 +499,7 @@ static bool is_supported_index_type(const TypePtr& t, const char** tname) auto rt = t->AsRecordType(); for ( auto i = 0; i < rt->NumFields(); ++i ) - if ( ! is_supported_index_type(rt->GetFieldType(i), tname) ) + if ( ! is_supported_index_type(rt->GetFieldType(i), tname, seen) ) return false; return true; @@ -499,7 +508,7 @@ static bool is_supported_index_type(const TypePtr& t, const char** tname) case TYPE_LIST: { for ( const auto& type : t->AsTypeList()->GetTypes() ) - if ( ! is_supported_index_type(type, tname) ) + if ( ! is_supported_index_type(type, tname, seen) ) return false; return true; @@ -509,7 +518,7 @@ static bool is_supported_index_type(const TypePtr& t, const char** tname) { auto tt = t->AsTableType(); - if ( ! is_supported_index_type(tt->GetIndices(), tname) ) + if ( ! is_supported_index_type(tt->GetIndices(), tname, seen) ) return false; const auto& yt = tt->Yield(); @@ -517,11 +526,11 @@ static bool is_supported_index_type(const TypePtr& t, const char** tname) if ( ! yt ) return true; - return is_supported_index_type(yt, tname); + return is_supported_index_type(yt, tname, seen); } case TYPE_VECTOR: - return is_supported_index_type(t->AsVectorType()->Yield(), tname); + return is_supported_index_type(t->AsVectorType()->Yield(), tname, seen); default: *tname = type_name(tag); @@ -545,7 +554,8 @@ TableType::TableType(TypeListPtr ind, TypePtr yield) if ( t == TYPE_INTERNAL_ERROR ) break; - if ( ! is_supported_index_type(tli, &unsupported_type_name) ) + std::unordered_set seen; + if ( ! is_supported_index_type(tli, &unsupported_type_name, seen) ) { auto msg = util::fmt("index type containing '%s' is not supported", unsupported_type_name); diff --git a/testing/btest/Baseline/language.record-recursive-table-2/output b/testing/btest/Baseline/language.record-recursive-table-2/output new file mode 100644 index 0000000000..0a6d1007b3 --- /dev/null +++ b/testing/btest/Baseline/language.record-recursive-table-2/output @@ -0,0 +1,7 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +===, 0, { + +} +===, 1, { +[[id=1, foo=]] = [id=2, foo=[id=1, foo=]] +} diff --git a/testing/btest/Baseline/language.record-recursive-table/output b/testing/btest/Baseline/language.record-recursive-table/output new file mode 100644 index 0000000000..2934a25b12 --- /dev/null +++ b/testing/btest/Baseline/language.record-recursive-table/output @@ -0,0 +1,38 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +===, 1, { +[[bar=1, parent={ + +}]] = { +1 +} +} +{ +[[bar=1, parent={ + +}]] = { +1 +} +} +===, 2, { +[[bar=1, parent={ + +}]] = { +1 +}, +[[bar=1, parent={ +[T] = [bar=2, parent={ + +}] +}]] = { +2 +} +} +===, 1, { +[[bar=1, parent={ +[T] = [bar=2, parent={ + +}] +}]] = { +2 +} +} diff --git a/testing/btest/language/record-recursive-table.zeek b/testing/btest/language/record-recursive-table.zeek new file mode 100644 index 0000000000..29163912c0 --- /dev/null +++ b/testing/btest/language/record-recursive-table.zeek @@ -0,0 +1,56 @@ +# @TEST-EXEC: zeek -b %INPUT >output +# @TEST-EXEC: btest-diff output + +type Foo: record { + bar: count; +}; + +redef record Foo += { + parent: table[bool] of Foo &default=table(); +}; + +event zeek_init() + { + local tbl: table[Foo] of set[count]; + local f = Foo($bar=1); + tbl[f] = set(1); + print "===", |tbl|, tbl; + print tbl; + + local parent_tbl: table[bool] of Foo = [ + [T] = Foo($bar=2), + ]; + f$parent = parent_tbl; + tbl[f] = set(2); + + # This now has two entries in the table, because + # after setting f$parent, that's a different key. + print "===", |tbl|, tbl; + + # Mutate f and use it to delete the first entry again. + f$parent = table(); + delete tbl[f]; + + # This will be size 1 + print "===", |tbl|, tbl; + } + +#@TEST-START-NEXT + +type Foo: record { + id: string; +}; + +redef record Foo += { + foo: Foo &optional; +}; + +event zeek_init() + { + local tbl: table[Foo] of Foo; + local f1 = Foo($id="1"); + local f2 = Foo($id="2", $foo=f1); + print "===", |tbl|, tbl; + tbl[f1] = f2; + print "===", |tbl|, tbl; + }