diff --git a/src/Attr.cc b/src/Attr.cc index 976fde083e..f4b9533918 100644 --- a/src/Attr.cc +++ b/src/Attr.cc @@ -498,51 +498,7 @@ void Attributes::CheckAttr(Attr* a) break; } - const auto& expire_func = a->GetExpr(); - - if ( expire_func->GetType()->Tag() != TYPE_FUNC ) - { - Error("&expire_func attribute is not a function"); - break; - } - - const FuncType* e_ft = expire_func->GetType()->AsFuncType(); - - if ( e_ft->Flavor() != FUNC_FLAVOR_FUNCTION ) - { - Error("&expire_func attribute is not a function"); - break; - } - - if ( e_ft->Yield()->Tag() != TYPE_INTERVAL ) - { - Error("&expire_func must yield a value of type interval"); - break; - } - - const TableType* the_table = type->AsTableType(); - - if (the_table->IsUnspecifiedTable()) - break; - - const auto& func_index_types = e_ft->ParamList()->GetTypes(); - // Keep backwards compatibility with idx: any idiom. - if ( func_index_types.size() == 2 ) - { - if (func_index_types[1]->Tag() == TYPE_ANY) - break; - } - - const auto& table_index_types = the_table->GetIndexTypes(); - - TypePList expected_args(1 + static_cast(table_index_types.size())); - expected_args.push_back(type->AsTableType()); - - for ( const auto& t : table_index_types ) - expected_args.push_back(t.get()); - - if ( ! e_ft->CheckArgs(&expected_args) ) - Error("&expire_func argument type clash"); + type->AsTableType()->CheckExpireFuncCompatibility({NewRef{}, a}); if ( Find(ATTR_BROKER_STORE ) ) Error("&broker_store and &expire_func cannot be used simultaneously"); diff --git a/src/Type.cc b/src/Type.cc index b9b230130c..5c0a1b8f0b 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -526,6 +526,61 @@ TableType::TableType(TypeListPtr ind, TypePtr yield) } } +bool TableType::CheckExpireFuncCompatibility(const detail::AttrPtr& attr) + { + assert(attr->Tag() == detail::ATTR_EXPIRE_FUNC); + + const auto& expire_func = attr->GetExpr(); + + if ( expire_func->GetType()->Tag() != TYPE_FUNC ) + { + attr->Error("&expire_func attribute is not a function"); + return false; + } + + const FuncType* e_ft = expire_func->GetType()->AsFuncType(); + + if ( e_ft->Flavor() != FUNC_FLAVOR_FUNCTION ) + { + attr->Error("&expire_func attribute is not a function"); + return false; + } + + if ( e_ft->Yield()->Tag() != TYPE_INTERVAL ) + { + attr->Error("&expire_func must yield a value of type interval"); + return false; + } + + if ( IsUnspecifiedTable() ) + return true; + + const auto& func_index_types = e_ft->ParamList()->GetTypes(); + // Keep backwards compatibility with idx: any idiom. + if ( func_index_types.size() == 2 ) + { + if ( func_index_types[1]->Tag() == TYPE_ANY ) + return true; + } + + const auto& table_index_types = GetIndexTypes(); + + std::vector expected_args; + expected_args.reserve(1 + table_index_types.size()); + expected_args.emplace_back(NewRef{}, this); + + for ( const auto& t : table_index_types ) + expected_args.emplace_back(t); + + if ( ! e_ft->CheckArgs(expected_args) ) + { + attr->Error("&expire_func argument type clash"); + return false; + } + + return true; + } + TypePtr TableType::ShallowClone() { return make_intrusive(indices, yield_type); diff --git a/src/Type.h b/src/Type.h index 574bf5d95d..43eefdd4c9 100644 --- a/src/Type.h +++ b/src/Type.h @@ -391,6 +391,15 @@ class TableType : public IndexType { public: TableType(TypeListPtr ind, TypePtr yield); + /** + * Assesses whether an &expire_func attribute's function type is compatible + * with this table type. + * @param attr the &expire_func attribute to check (this method must not be + * called with other type of attributes). + * @return true if compatible, false if not + */ + bool CheckExpireFuncCompatibility(const detail::AttrPtr& attr); + TypePtr ShallowClone() override; // Returns true if this table type is "unspecified", which is diff --git a/src/Val.cc b/src/Val.cc index e3eb35002f..600dee1c41 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -1502,7 +1502,12 @@ void TableVal::SetAttrs(detail::AttributesPtr a) const auto& ef = attrs->Find(detail::ATTR_EXPIRE_FUNC); if ( ef ) - expire_func = ef->GetExpr(); + { + if ( GetType()->AsTableType()->CheckExpireFuncCompatibility(ef) ) + expire_func = ef->GetExpr(); + else + expire_func = nullptr; + } const auto& cf = attrs->Find(detail::ATTR_ON_CHANGE); diff --git a/testing/btest/Baseline/language.expire-func-type-check/output b/testing/btest/Baseline/language.expire-func-type-check/output index 8fcc024a4e..d848bce4ac 100644 --- a/testing/btest/Baseline/language.expire-func-type-check/output +++ b/testing/btest/Baseline/language.expire-func-type-check/output @@ -1,4 +1,14 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. error in <...>/expire-func-type-check.zeek, line 12: &expire_func attribute is not a function (&expire_func=myev) +error in <...>/expire-func-type-check.zeek, line 12: &expire_func attribute is not a function (&expire_func=myev) +error in <...>/expire-func-type-check.zeek, line 12: &expire_func attribute is not a function (&expire_func=myev) +error in <...>/expire-func-type-check.zeek, line 13: &expire_func attribute is not a function (&expire_func=myhk) +error in <...>/expire-func-type-check.zeek, line 13: &expire_func attribute is not a function (&expire_func=myhk) error in <...>/expire-func-type-check.zeek, line 13: &expire_func attribute is not a function (&expire_func=myhk) error in <...>/expire-func-type-check.zeek, line 14: &expire_func attribute is not a function (&expire_func=foo) +error in <...>/expire-func-type-check.zeek, line 14: &expire_func attribute is not a function (&expire_func=foo) +error in <...>/expire-func-type-check.zeek, line 14: &expire_func attribute is not a function (&expire_func=foo) +warning in <...>/expire-func-type-check.zeek, line 17: Wrong number of arguments for function. Expected 3, got 2. (function(t:table[addr,port] of set[addr]; s:set[addr,port];) : interval) +error in <...>/expire-func-type-check.zeek, line 18: &expire_func argument type clash (&expire_func=expire_ce) +warning in <...>/expire-func-type-check.zeek, line 17: Wrong number of arguments for function. Expected 3, got 2. (function(t:table[addr,port] of set[addr]; s:set[addr,port];) : interval) +error in <...>/expire-func-type-check.zeek, line 18: &expire_func argument type clash (&expire_func=expire_ce) diff --git a/testing/btest/language/expire-func-type-check.zeek b/testing/btest/language/expire-func-type-check.zeek index a6126806f2..189d10d311 100644 --- a/testing/btest/language/expire-func-type-check.zeek +++ b/testing/btest/language/expire-func-type-check.zeek @@ -12,3 +12,7 @@ global foo = 3; global t: table[string] of count &expire_func=myev; global tt: table[string] of count &expire_func=myhk; global ttt: table[string] of count &expire_func=foo; + +type ap: set[addr, port]; +global expire_ce: function(t: table[addr,port] of set[addr], s: ap): interval; +global ce: table[addr, port] of set[addr]=table() &create_expire=1 secs &expire_func=expire_ce;