diff --git a/src/Expr.cc b/src/Expr.cc index 2bb0e323c0..7ae4bb67b9 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -3218,12 +3218,16 @@ void HasFieldExpr::ExprDescribe(ODesc* d) const d->Add(field); } + RecordConstructorExpr::RecordConstructorExpr(ListExprPtr constructor_list) - : Expr(EXPR_RECORD_CONSTRUCTOR), op(std::move(constructor_list)) + : Expr(EXPR_RECORD_CONSTRUCTOR) { if ( IsError() ) return; + map = std::nullopt; + op = std::move(constructor_list); + // Spin through the list, which should be comprised only of // record-field-assign expressions, and build up a // record type to associate with this constructor. @@ -3248,8 +3252,46 @@ RecordConstructorExpr::RecordConstructorExpr(ListExprPtr constructor_list) SetType(make_intrusive(record_types)); } -RecordConstructorExpr::~RecordConstructorExpr() +RecordConstructorExpr::RecordConstructorExpr(RecordTypePtr known_rt, + ListExprPtr constructor_list) +: Expr(EXPR_RECORD_CONSTRUCTOR) { + if ( IsError() ) + return; + + SetType(known_rt); + op = std::move(constructor_list); + + const auto& exprs = op->AsListExpr()->Exprs(); + map = std::vector(exprs.length()); + + int i = 0; + for ( const auto& e : exprs ) + { + if ( e->Tag() != EXPR_FIELD_ASSIGN ) + { + Error("bad type in record constructor", e); + SetError(); + continue; + } + + auto field = e->AsFieldAssignExpr(); + int index = known_rt->FieldOffset(field->FieldName()); + + if ( index < 0 ) + { + Error("no such field in record", e); + SetError(); + continue; + } + + auto known_ft = known_rt->GetFieldType(index); + + if ( ! field->PromoteTo(known_ft) ) + SetError(); + + (*map)[i++] = index; + } } ValPtr RecordConstructorExpr::InitVal(const zeek::Type* t, ValPtr aggr) const @@ -3289,13 +3331,16 @@ ValPtr RecordConstructorExpr::Eval(Frame* f) const const auto& exprs = op->Exprs(); auto rt = cast_intrusive(type); - if ( exprs.length() != rt->NumFields() ) + if ( ! map && exprs.length() != rt->NumFields() ) RuntimeErrorWithCallStack("inconsistency evaluating record constructor"); auto rv = make_intrusive(std::move(rt)); for ( int i = 0; i < exprs.length(); ++i ) - rv->Assign(i, exprs[i]->Eval(f)); + { + int ind = map ? (*map)[i] : i; + rv->Assign(ind, exprs[i]->Eval(f)); + } return rv; } @@ -3697,6 +3742,12 @@ FieldAssignExpr::FieldAssignExpr(const char* arg_field_name, ExprPtr value) SetType(op->GetType()); } +bool FieldAssignExpr::PromoteTo(TypePtr t) + { + op = check_and_promote_expr(op.get(), t.get()); + return op != nullptr; + } + void FieldAssignExpr::EvalIntoAggregate(const zeek::Type* t, Val* aggr, Frame* f) const { diff --git a/src/Expr.h b/src/Expr.h index 91a0cc3750..13e31a4d75 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -1062,9 +1062,13 @@ protected: class RecordConstructorExpr final : public Expr { public: explicit RecordConstructorExpr(ListExprPtr constructor_list); - ~RecordConstructorExpr() override; - ListExpr* Op() const { return op.get(); } + // This form is used to construct records of a known (ultimate) type. + explicit RecordConstructorExpr(RecordTypePtr known_rt, + ListExprPtr constructor_list); + + ListExprPtr Op() const { return op; } + const auto& Map() const { return map; } ValPtr Eval(Frame* f) const override; @@ -1085,6 +1089,7 @@ protected: void ExprDescribe(ODesc* d) const override; ListExprPtr op; + std::optional> map; }; class TableConstructorExpr final : public UnaryExpr { @@ -1165,6 +1170,14 @@ public: const char* FieldName() const { return field_name.c_str(); } + // When these are first constructed, we don't know the type. + // The following method coerces/promotes the assignment expression + // as needed, once we do know the type. + // + // Returns true on success, false if the types were incompatible + // (in which case an error is reported). + bool PromoteTo(TypePtr t); + void EvalIntoAggregate(const zeek::Type* t, Val* aggr, Frame* f) const override; bool IsRecordElement(TypeDecl* td) const override; diff --git a/src/parse.y b/src/parse.y index bea4b90066..7ce977d2d4 100644 --- a/src/parse.y +++ b/src/parse.y @@ -683,10 +683,8 @@ expr: switch ( ctor_type->Tag() ) { case TYPE_RECORD: { - auto rce = make_intrusive( - ListExprPtr{AdoptRef{}, $4}); auto rt = cast_intrusive(ctor_type); - $$ = new RecordCoerceExpr(std::move(rce), std::move(rt)); + $$ = new RecordConstructorExpr(rt, ListExprPtr{AdoptRef{}, $4}); } break; diff --git a/src/script_opt/Expr.cc b/src/script_opt/Expr.cc index e8524e93fe..8a30d4478e 100644 --- a/src/script_opt/Expr.cc +++ b/src/script_opt/Expr.cc @@ -1827,7 +1827,14 @@ ExprPtr HasFieldExpr::Duplicate() ExprPtr RecordConstructorExpr::Duplicate() { auto op_l = op->Duplicate()->AsListExprPtr(); - return SetSucc(new RecordConstructorExpr(op_l)); + + if ( map ) + { + auto rt = cast_intrusive(type); + return SetSucc(new RecordConstructorExpr(rt, op_l)); + } + else + return SetSucc(new RecordConstructorExpr(op_l)); } bool RecordConstructorExpr::HasReducedOps(Reducer* c) const diff --git a/src/script_opt/GenRDs.cc b/src/script_opt/GenRDs.cc index e42d21856d..a2f7000652 100644 --- a/src/script_opt/GenRDs.cc +++ b/src/script_opt/GenRDs.cc @@ -1084,7 +1084,7 @@ TraversalCode RD_Decorate::PreExpr(const Expr* e) { auto r = static_cast(e); auto l = r->Op(); - mgr.SetPreFromPre(l, e); + mgr.SetPreFromPre(l.get(), e); break; } diff --git a/src/script_opt/UseDefs.cc b/src/script_opt/UseDefs.cc index c26ad73f09..a30b8cca3b 100644 --- a/src/script_opt/UseDefs.cc +++ b/src/script_opt/UseDefs.cc @@ -482,7 +482,7 @@ UDs UseDefs::ExprUDs(const Expr* e) case EXPR_RECORD_CONSTRUCTOR: { auto r = static_cast(e); - AddInExprUDs(uds, r->Op()); + AddInExprUDs(uds, r->Op().get()); break; }