diff --git a/src/Expr.cc b/src/Expr.cc index dc47340ccd..cc93c47be0 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -3971,12 +3971,18 @@ RecordCoerceExpr::RecordCoerceExpr(Expr* op, RecordType* r) if ( ! same_type(sup_t_i, sub_t_i) ) { - char buf[512]; - safe_snprintf(buf, sizeof(buf), - "type clash for field \"%s\"", sub_r->FieldName(i)); - Error(buf, sub_t_i); - SetError(); - break; + if ( sup_t_i->Tag() != TYPE_RECORD || + sub_t_i->Tag() != TYPE_RECORD || + ! record_promotion_compatible(sup_t_i->AsRecordType(), + sub_t_i->AsRecordType()) ) + { + char buf[512]; + safe_snprintf(buf, sizeof(buf), + "type clash for field \"%s\"", sub_r->FieldName(i)); + Error(buf, sub_t_i); + SetError(); + break; + } } map[t_i] = i; @@ -4024,6 +4030,24 @@ Val* RecordCoerceExpr::Fold(Val* v) const rhs = rhs->Ref(); assert(rhs || Type()->AsRecordType()->FieldDecl(i)->FindAttr(ATTR_OPTIONAL)); + + BroType* rhs_type = rhs->Type(); + RecordType* val_type = val->Type()->AsRecordType(); + BroType* field_type = val_type->FieldType(i); + + if ( rhs_type->Tag() == TYPE_RECORD && + field_type->Tag() == TYPE_RECORD && + ! same_type(rhs_type, field_type) ) + { + Val* new_val = rhs->AsRecordVal()->CoerceTo( + field_type->AsRecordType()); + if ( new_val ) + { + Unref(rhs); + rhs = new_val; + } + } + val->Assign(i, rhs); } else diff --git a/testing/btest/Baseline/language.record-recursive-coercion/output b/testing/btest/Baseline/language.record-recursive-coercion/output index 37c916713f..5c4dea621c 100644 --- a/testing/btest/Baseline/language.record-recursive-coercion/output +++ b/testing/btest/Baseline/language.record-recursive-coercion/output @@ -1 +1,2 @@ [major=4, minor=4, minor2=, addl=] +[c=1, f=[i=2.0 hrs, s=]] diff --git a/testing/btest/language/record-recursive-coercion.bro b/testing/btest/language/record-recursive-coercion.bro index eda80e3d11..ad9e41bd3a 100644 --- a/testing/btest/language/record-recursive-coercion.bro +++ b/testing/btest/language/record-recursive-coercion.bro @@ -1,4 +1,4 @@ -# @TEST-EXEC: bro %INPUT >output 2>&1 +# @TEST-EXEC: bro %INPUT >output # @TEST-EXEC: btest-diff output type Version: record { @@ -17,8 +17,24 @@ global matched_software: table[string] of Info = { ["OpenSSH_4.4"] = [$name="OpenSSH", $version=[$major=4,$minor=4]], }; +type Foo: record { + i: interval &default=1hr; + s: string &optional; +}; + +type FooContainer: record { + c: count; + f: Foo &optional; +}; + +function foo_func(fc: FooContainer) + { + print fc; + } + event bro_init() { for ( sw in matched_software ) print matched_software[sw]$version; + foo_func([$c=1, $f=[$i=2hrs]]); }