diff options
author | Philip Herron <philip.herron@embecosm.com> | 2021-01-18 21:42:53 +0000 |
---|---|---|
committer | Philip Herron <herron.philip@googlemail.com> | 2021-01-20 09:59:22 +0000 |
commit | 4f6f18fe296d4729648eec0b77c8610f0b43445c (patch) | |
tree | a3897a28c43df3381df8bee90ab0a9501f32dd68 | |
parent | 12f7bd0fc4e9ab4e98869b5265aea6bacfb31d03 (diff) | |
download | gcc-4f6f18fe296d4729648eec0b77c8610f0b43445c.zip gcc-4f6f18fe296d4729648eec0b77c8610f0b43445c.tar.gz gcc-4f6f18fe296d4729648eec0b77c8610f0b43445c.tar.bz2 |
Support struct base reference in constructors
This creates implicit FieldAccessExpr or TupleIndexExprs for the missing
fields in the constructor using the base struct as the receiver.
-rw-r--r-- | gcc/rust/hir/tree/rust-hir-expr.h | 10 | ||||
-rw-r--r-- | gcc/rust/hir/tree/rust-hir.h | 6 | ||||
-rw-r--r-- | gcc/rust/resolve/rust-ast-resolve-expr.h | 8 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check.cc | 115 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-tyty.h | 9 | ||||
-rw-r--r-- | gcc/testsuite/rust.test/compilable/struct_init_4.rs | 9 | ||||
-rw-r--r-- | gcc/testsuite/rust.test/compilable/struct_init_5.rs | 9 | ||||
-rw-r--r-- | gcc/testsuite/rust.test/compilable/struct_init_6.rs | 9 |
8 files changed, 156 insertions, 19 deletions
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h index ff3fbab..29b5691 100644 --- a/gcc/rust/hir/tree/rust-hir-expr.h +++ b/gcc/rust/hir/tree/rust-hir-expr.h @@ -1669,6 +1669,16 @@ public: } } + std::vector<std::unique_ptr<StructExprField> > &get_fields () + { + return fields; + }; + + const std::vector<std::unique_ptr<StructExprField> > &get_fields () const + { + return fields; + }; + protected: /* Use covariance to implement clone function as returning this object rather * than base */ diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h index 5d758ee..fabc31d 100644 --- a/gcc/rust/hir/tree/rust-hir.h +++ b/gcc/rust/hir/tree/rust-hir.h @@ -819,6 +819,9 @@ public: const Analysis::NodeMapping &get_mappings () const { return mappings; } + // Clone function implementation as pure virtual method + virtual Expr *clone_expr_impl () const = 0; + protected: // Constructor Expr (Analysis::NodeMapping mappings, @@ -826,9 +829,6 @@ protected: : outer_attrs (std::move (outer_attribs)), mappings (std::move (mappings)) {} - // Clone function implementation as pure virtual method - virtual Expr *clone_expr_impl () const = 0; - // TODO: think of less hacky way to implement this kind of thing // Sets outer attributes. void set_outer_attrs (std::vector<Attribute> outer_attrs_to_set) diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.h b/gcc/rust/resolve/rust-ast-resolve-expr.h index 91ed6e0..181124a 100644 --- a/gcc/rust/resolve/rust-ast-resolve-expr.h +++ b/gcc/rust/resolve/rust-ast-resolve-expr.h @@ -179,6 +179,14 @@ public: { ResolveExpr::go (&struct_expr.get_struct_name (), struct_expr.get_node_id ()); + + if (struct_expr.has_struct_base ()) + { + AST::StructBase &base = struct_expr.get_struct_base (); + ResolveExpr::go (base.get_base_struct ().get (), + struct_expr.get_node_id ()); + } + struct_expr.iterate ( [&] (AST::StructExprField *struct_field) mutable -> bool { ResolveStructExprField::go (struct_field, struct_expr.get_node_id ()); diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc index 6f8c3a1..4c09992 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.cc +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -89,6 +89,22 @@ TypeCheckStructExpr::visit (HIR::StructExprStructFields &struct_expr) return; } + resolved = struct_path_resolved; + if (struct_expr.has_struct_base ()) + { + TyTy::TyBase *base_resolved + = TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ()); + resolved = struct_path_resolved->combine (base_resolved); + if (resolved == nullptr) + { + rust_fatal_error ( + struct_expr.struct_base->base_struct->get_locus_slow (), + "incompatible types for base struct reference"); + return; + } + } + + bool ok = true; struct_expr.iterate ([&] (HIR::StructExprField *field) mutable -> bool { resolved_field = nullptr; field->accept_vis (*this); @@ -96,6 +112,7 @@ TypeCheckStructExpr::visit (HIR::StructExprStructFields &struct_expr) { rust_fatal_error (field->get_locus (), "failed to resolve type for field"); + ok = false; return false; } @@ -103,28 +120,94 @@ TypeCheckStructExpr::visit (HIR::StructExprStructFields &struct_expr) return true; }); - TyTy::TyBase *expr_type = struct_path_resolved; - if (struct_expr.has_struct_base ()) + // something failed setting up the fields + if (!ok) + return; + + // check the arguments are all assigned and fix up the ordering + if (fields_assigned.size () != struct_path_resolved->num_fields ()) { - TyTy::TyBase *base_resolved - = TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ()); - expr_type = expr_type->combine (base_resolved); - if (resolved == nullptr) + if (!struct_expr.has_struct_base ()) { - rust_fatal_error ( - struct_expr.struct_base->base_struct->get_locus_slow (), - "incompatible types for base struct reference"); + rust_error_at (struct_expr.get_locus (), + "constructor is missing fields"); return; } + else + { + // we have a struct base to assign the missing fields from. + // the missing fields can be implicit FieldAccessExprs for the value + std::set<std::string> missing_fields; + struct_path_resolved->iterate_fields ( + [&] (TyTy::StructFieldType *field) mutable -> bool { + auto it = fields_assigned.find (field->get_name ()); + if (it == fields_assigned.end ()) + missing_fields.insert (field->get_name ()); + return true; + }); + + // we can generate FieldAccessExpr or TupleAccessExpr for the values + // of the missing fields. + for (auto &missing : missing_fields) + { + HIR::Expr *receiver + = struct_expr.struct_base->base_struct->clone_expr_impl (); + + HIR::StructExprField *implicit_field = nullptr; + if (struct_path_resolved->is_tuple_struct ()) + { + std::vector<HIR::Attribute> outer_attribs; + TupleIndex tuple_index = std::stoi (missing); + + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping ( + crate_num, + struct_expr.struct_base->base_struct->get_mappings () + .get_nodeid (), + mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID); + + HIR::Expr *field_value = new HIR::TupleIndexExpr ( + mapping, std::unique_ptr<HIR::Expr> (receiver), tuple_index, + std::move (outer_attribs), + struct_expr.struct_base->base_struct->get_locus_slow ()); + + implicit_field = new HIR::StructExprFieldIndexValue ( + mapping, tuple_index, + std::unique_ptr<HIR::Expr> (field_value), + struct_expr.struct_base->base_struct->get_locus_slow ()); + } + else + { + std::vector<HIR::Attribute> outer_attribs; + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping ( + crate_num, + struct_expr.struct_base->base_struct->get_mappings () + .get_nodeid (), + mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID); + + HIR::Expr *field_value = new HIR::FieldAccessExpr ( + mapping, std::unique_ptr<HIR::Expr> (receiver), missing, + std::move (outer_attribs), + struct_expr.struct_base->base_struct->get_locus_slow ()); + + implicit_field = new HIR::StructExprFieldIdentifierValue ( + mapping, missing, std::unique_ptr<HIR::Expr> (field_value), + struct_expr.struct_base->base_struct->get_locus_slow ()); + } + + struct_expr.get_fields ().push_back ( + std::unique_ptr<HIR::StructExprField> (implicit_field)); + } + } } - else if (fields_assigned.size () != struct_path_resolved->num_fields ()) - { - rust_fatal_error (struct_expr.get_locus (), - "some fields are not fully assigned"); - return; - } - resolved = expr_type; + // everything is ok, now we need to ensure all field values are ordered + // correctly. The GIMPLE backend uses a simple algorithm that assumes each + // assigned field in the constructor is in the same order as the field in the + // type + + // TODO } void diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index b0ccf24..c32ac02 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -219,6 +219,15 @@ public: std::vector<StructFieldType *> &get_fields () { return fields; } const std::vector<StructFieldType *> &get_fields () const { return fields; } + void iterate_fields (std::function<bool (StructFieldType *)> cb) + { + for (auto &f : fields) + { + if (!cb (f)) + return; + } + } + private: std::string identifier; bool is_tuple; diff --git a/gcc/testsuite/rust.test/compilable/struct_init_4.rs b/gcc/testsuite/rust.test/compilable/struct_init_4.rs new file mode 100644 index 0000000..2cc8a19 --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/struct_init_4.rs @@ -0,0 +1,9 @@ +struct Foo { + a: i32, + b: i32, +} + +fn main() { + let a = Foo { a: 1, b: 2 }; + let b = Foo { a: 3, b: 4, ..a }; +} diff --git a/gcc/testsuite/rust.test/compilable/struct_init_5.rs b/gcc/testsuite/rust.test/compilable/struct_init_5.rs new file mode 100644 index 0000000..c79ea71 --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/struct_init_5.rs @@ -0,0 +1,9 @@ +struct Foo { + a: i32, + b: i32, +} + +fn main() { + let a = Foo { a: 1, b: 2 }; + let b = Foo { ..a }; +} diff --git a/gcc/testsuite/rust.test/compilable/struct_init_6.rs b/gcc/testsuite/rust.test/compilable/struct_init_6.rs new file mode 100644 index 0000000..51b90bc --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/struct_init_6.rs @@ -0,0 +1,9 @@ +struct Foo { + a: i32, + b: i32, +} + +fn main() { + let a = Foo { a: 1, b: 2 }; + let b = Foo { a: 1, ..a }; +} |