From 0b3d408137b9906df68f3fa5729dce7e8760b263 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 19:09:34 +0200 Subject: [PATCH 01/16] borrowck: Add initial structure for borrowchecking gcc/rust/ChangeLog: * Make-lang.in: Build borrowck. * checks/errors/borrowck/rust-borrow-checker.cc: New file. * checks/errors/borrowck/rust-borrow-checker.h: New file. * checks/errors/borrowck/rust-function-collector.h: New file. Signed-off-by: Jakub Dupak --- gcc/rust/Make-lang.in | 7 + .../errors/borrowck/rust-borrow-checker.cc | 43 ++++ .../errors/borrowck/rust-borrow-checker.h | 39 ++++ .../errors/borrowck/rust-function-collector.h | 199 ++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100644 gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc create mode 100644 gcc/rust/checks/errors/borrowck/rust-borrow-checker.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-function-collector.h diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index e6a2099f043c..f4b9eb5502e2 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -148,6 +148,7 @@ GRS_OBJS = \ rust/rust-hir-type-check-stmt.o \ rust/rust-hir-type-check-enumitem.o \ rust/rust-hir-type-check-implitem.o \ + rust/rust-borrow-checker.o \ rust/rust-hir-dot-operator.o \ rust/rust-hir-path-probe.o \ rust/rust-type-util.o \ @@ -371,6 +372,7 @@ RUST_INCLUDES = -I $(srcdir)/rust \ -I $(srcdir)/rust/checks/lints \ -I $(srcdir)/rust/checks/errors \ -I $(srcdir)/rust/checks/errors/privacy \ + -I $(srcdir)/rust/checks/errors/borrowck \ -I $(srcdir)/rust/util \ -I $(srcdir)/rust/metadata \ -I $(srcdir)/../libgrust @@ -448,6 +450,11 @@ rust/%.o: rust/checks/errors/privacy/%.cc $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $< $(POSTCOMPILE) +# build borrow checking pass files in rust folder +rust/%.o: rust/checks/errors/borrowck/%.cc + $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $< + $(POSTCOMPILE) + # build rust/metadata files in rust folder rust/%.o: rust/metadata/%.cc $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $< diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc new file mode 100644 index 000000000000..a6086b8a6956 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -0,0 +1,43 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-borrow-checker.h" +#include "rust-function-collector.h" + +namespace Rust { +namespace HIR { + +BorrowChecker::BorrowChecker () = default; + +void +BorrowChecker::go (HIR::Crate &crate) +{ + FunctionCollector collector; + collector.go (crate); + + for (auto func ATTRIBUTE_UNUSED : collector.get_functions ()) + { + } + + for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ()) + { + } +} + +} // namespace HIR +} // namespace Rust \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h new file mode 100644 index 000000000000..7df5fe788a0b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h @@ -0,0 +1,39 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_BORROW_CHECK_H +#define RUST_HIR_BORROW_CHECK_H + +#include "rust-hir.h" + +namespace Rust { +namespace HIR { + +class BorrowChecker +{ +public: + BorrowChecker (); + + /** Perform borrow-checking using polonius on an entire crate */ + void go (HIR::Crate &crate); +}; + +} // namespace HIR +} // namespace Rust + +#endif // RUST_HIR_BORROW_CHECK_H diff --git a/gcc/rust/checks/errors/borrowck/rust-function-collector.h b/gcc/rust/checks/errors/borrowck/rust-function-collector.h new file mode 100644 index 000000000000..f8d36ffe2b42 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-function-collector.h @@ -0,0 +1,199 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_FUNCTION_COLLECTOR_H +#define RUST_HIR_FUNCTION_COLLECTOR_H + +#include "rust-hir-item.h" +#include "rust-hir-visitor.h" +#include "rust-hir.h" + +#include + +namespace Rust { + +// Collects all HIR items eligible for borrow checking. +class FunctionCollector : public HIR::HIRFullVisitor +{ + std::vector functions; + std::vector closures; + +public: + void go (HIR::Crate &crate) { visit_all (crate.get_items ()); } + + WARN_UNUSED_RESULT const std::vector &get_functions () const + { + return functions; + } + + WARN_UNUSED_RESULT const std::vector & + get_closures () const + { + return closures; + } + +protected: + template void visit_all (std::vector> &items) + { + for (std::unique_ptr &item : items) + { + item->accept_vis (*this); + } + } + + void visit (HIR::Function &function) override + { + functions.push_back (&function); + function.get_definition ()->accept_vis (*this); + } + + void visit (HIR::ClosureExpr &closure) override + { + closures.push_back (&closure); + closure.get_expr ()->accept_vis (*this); + } + + // TODO: recurse for nested closures and functions. +public: + void visit (HIR::Lifetime &lifetime) override {} + void visit (HIR::LifetimeParam &lifetime_param) override {} + void visit (HIR::PathInExpression &path) override {} + void visit (HIR::TypePathSegment &segment) override {} + void visit (HIR::TypePathSegmentGeneric &segment) override {} + void visit (HIR::TypePathSegmentFunction &segment) override {} + void visit (HIR::TypePath &path) override {} + void visit (HIR::QualifiedPathInExpression &path) override {} + void visit (HIR::QualifiedPathInType &path) override {} + void visit (HIR::LiteralExpr &expr) override {} + void visit (HIR::BorrowExpr &expr) override {} + void visit (HIR::DereferenceExpr &expr) override {} + void visit (HIR::ErrorPropagationExpr &expr) override {} + void visit (HIR::NegationExpr &expr) override {} + void visit (HIR::ArithmeticOrLogicalExpr &expr) override {} + void visit (HIR::ComparisonExpr &expr) override {} + void visit (HIR::LazyBooleanExpr &expr) override {} + void visit (HIR::TypeCastExpr &expr) override {} + void visit (HIR::AssignmentExpr &expr) override {} + void visit (HIR::CompoundAssignmentExpr &expr) override {} + void visit (HIR::GroupedExpr &expr) override {} + void visit (HIR::ArrayElemsValues &elems) override {} + void visit (HIR::ArrayElemsCopied &elems) override {} + void visit (HIR::ArrayExpr &expr) override {} + void visit (HIR::ArrayIndexExpr &expr) override {} + void visit (HIR::TupleExpr &expr) override {} + void visit (HIR::TupleIndexExpr &expr) override {} + void visit (HIR::StructExprStruct &expr) override {} + void visit (HIR::StructExprFieldIdentifier &field) override {} + void visit (HIR::StructExprFieldIdentifierValue &field) override {} + void visit (HIR::StructExprFieldIndexValue &field) override {} + void visit (HIR::StructExprStructFields &expr) override {} + void visit (HIR::StructExprStructBase &expr) override {} + void visit (HIR::CallExpr &expr) override {} + void visit (HIR::MethodCallExpr &expr) override {} + void visit (HIR::FieldAccessExpr &expr) override {} + void visit (HIR::BlockExpr &expr) override {} + void visit (HIR::ContinueExpr &expr) override {} + void visit (HIR::BreakExpr &expr) override {} + void visit (HIR::RangeFromToExpr &expr) override {} + void visit (HIR::RangeFromExpr &expr) override {} + void visit (HIR::RangeToExpr &expr) override {} + void visit (HIR::RangeFullExpr &expr) override {} + void visit (HIR::RangeFromToInclExpr &expr) override {} + void visit (HIR::RangeToInclExpr &expr) override {} + void visit (HIR::ReturnExpr &expr) override {} + void visit (HIR::UnsafeBlockExpr &expr) override {} + void visit (HIR::LoopExpr &expr) override {} + void visit (HIR::WhileLoopExpr &expr) override {} + void visit (HIR::WhileLetLoopExpr &expr) override {} + void visit (HIR::IfExpr &expr) override {} + void visit (HIR::IfExprConseqElse &expr) override {} + void visit (HIR::IfLetExpr &expr) override {} + void visit (HIR::IfLetExprConseqElse &expr) override {} + void visit (HIR::MatchExpr &expr) override {} + void visit (HIR::AwaitExpr &expr) override {} + void visit (HIR::AsyncBlockExpr &expr) override {} + void visit (HIR::TypeParam ¶m) override {} + void visit (HIR::ConstGenericParam ¶m) override {} + void visit (HIR::LifetimeWhereClauseItem &item) override {} + void visit (HIR::TypeBoundWhereClauseItem &item) override {} + void visit (HIR::Module &module) override {} + void visit (HIR::ExternCrate &crate) override {} + void visit (HIR::UseTreeGlob &use_tree) override {} + void visit (HIR::UseTreeList &use_tree) override {} + void visit (HIR::UseTreeRebind &use_tree) override {} + void visit (HIR::UseDeclaration &use_decl) override {} + void visit (HIR::TypeAlias &type_alias) override {} + void visit (HIR::StructStruct &struct_item) override {} + void visit (HIR::TupleStruct &tuple_struct) override {} + void visit (HIR::EnumItem &item) override {} + void visit (HIR::EnumItemTuple &item) override {} + void visit (HIR::EnumItemStruct &item) override {} + void visit (HIR::EnumItemDiscriminant &item) override {} + void visit (HIR::Enum &enum_item) override {} + void visit (HIR::Union &union_item) override {} + void visit (HIR::ConstantItem &const_item) override {} + void visit (HIR::StaticItem &static_item) override {} + void visit (HIR::TraitItemFunc &item) override {} + void visit (HIR::TraitItemConst &item) override {} + void visit (HIR::TraitItemType &item) override {} + void visit (HIR::Trait &trait) override {} + void visit (HIR::ImplBlock &impl) override {} + void visit (HIR::ExternalStaticItem &item) override {} + void visit (HIR::ExternalFunctionItem &item) override {} + void visit (HIR::ExternBlock &block) override {} + void visit (HIR::LiteralPattern &pattern) override {} + void visit (HIR::IdentifierPattern &pattern) override {} + void visit (HIR::WildcardPattern &pattern) override {} + void visit (HIR::RangePatternBoundLiteral &bound) override {} + void visit (HIR::RangePatternBoundPath &bound) override {} + void visit (HIR::RangePatternBoundQualPath &bound) override {} + void visit (HIR::RangePattern &pattern) override {} + void visit (HIR::ReferencePattern &pattern) override {} + void visit (HIR::StructPatternFieldTuplePat &field) override {} + void visit (HIR::StructPatternFieldIdentPat &field) override {} + void visit (HIR::StructPatternFieldIdent &field) override {} + void visit (HIR::StructPattern &pattern) override {} + void visit (HIR::TupleStructItemsNoRange &tuple_items) override {} + void visit (HIR::TupleStructItemsRange &tuple_items) override {} + void visit (HIR::TupleStructPattern &pattern) override {} + void visit (HIR::TuplePatternItemsMultiple &tuple_items) override {} + void visit (HIR::TuplePatternItemsRanged &tuple_items) override {} + void visit (HIR::TuplePattern &pattern) override {} + void visit (HIR::SlicePattern &pattern) override {} + void visit (HIR::AltPattern &pattern) override {} + void visit (HIR::EmptyStmt &stmt) override {} + void visit (HIR::LetStmt &stmt) override {} + void visit (HIR::ExprStmt &stmt) override {} + void visit (HIR::TraitBound &bound) override {} + void visit (HIR::ImplTraitType &type) override {} + void visit (HIR::TraitObjectType &type) override {} + void visit (HIR::ParenthesisedType &type) override {} + void visit (HIR::ImplTraitTypeOneBound &type) override {} + void visit (HIR::TupleType &type) override {} + void visit (HIR::NeverType &type) override {} + void visit (HIR::RawPointerType &type) override {} + void visit (HIR::ReferenceType &type) override {} + void visit (HIR::ArrayType &type) override {} + void visit (HIR::SliceType &type) override {} + void visit (HIR::InferredType &type) override {} + void visit (HIR::BareFunctionType &type) override {} +}; + +} // namespace Rust + +#endif // RUST_HIR_FUNCTION_COLLECTOR_H From 193cb2e44fbb59bb8ed30d0c118e8ee2339f2c70 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Thu, 19 Oct 2023 10:59:54 +0200 Subject: [PATCH 02/16] borrowck: Add CLI option for borrowck gcc/rust/ChangeLog: * checks/errors/borrowck/rust-borrow-checker.cc (BorrowChecker::BorrowChecker): Opt dump. (BorrowChecker::go): Opt dump. * checks/errors/borrowck/rust-borrow-checker.h (class BorrowChecker): Opt dump. * lang.opt: Add compile until borrowcheck. * rust-session-manager.cc (Session::enable_dump): Add BIR. (Session::compile_crate): Handle new options. * rust-session-manager.h (struct CompileOptions): Add BIR. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-borrow-checker.cc | 2 -- .../checks/errors/borrowck/rust-borrow-checker.h | 5 ++++- gcc/rust/lang.opt | 7 +++++-- gcc/rust/rust-session-manager.cc | 14 +++++++++++++- gcc/rust/rust-session-manager.h | 3 +++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index a6086b8a6956..6c2922310423 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -22,8 +22,6 @@ namespace Rust { namespace HIR { -BorrowChecker::BorrowChecker () = default; - void BorrowChecker::go (HIR::Crate &crate) { diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h index 7df5fe788a0b..549af3560e9b 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.h @@ -26,8 +26,11 @@ namespace HIR { class BorrowChecker { + bool enable_dump_bir; + public: - BorrowChecker (); + explicit BorrowChecker (bool enable_dump_bir) + : enable_dump_bir (enable_dump_bir){}; /** Perform borrow-checking using polonius on an entire crate */ void go (HIR::Crate &crate); diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index a77c0a903947..67285a6d92b2 100644 --- a/gcc/rust/lang.opt +++ b/gcc/rust/lang.opt @@ -190,10 +190,13 @@ EnumValue Enum(frust_compile_until) String(const) Value(8) EnumValue -Enum(frust_compile_until) String(compilation) Value(9) +Enum(frust_compile_until) String(borrowcheck) Value(9) EnumValue -Enum(frust_compile_until) String(end) Value(10) +Enum(frust_compile_until) String(compilation) Value(10) + +EnumValue +Enum(frust_compile_until) String(end) Value(11) frust-name-resolution-2.0 Rust Var(flag_name_resolution_2_0) diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index c674a18d1c92..2843b7d25cf2 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -51,6 +51,7 @@ #include "selftest.h" #include "tm.h" #include "rust-target.h" +#include "rust-borrow-checker.h" extern bool saw_errors (void); @@ -313,7 +314,7 @@ Session::enable_dump (std::string arg) "dump option was not given a name. choose %, %, " "%, %, " "%, %, %, %, " - "%, or %"); + "%, % or %"); return false; } @@ -357,6 +358,10 @@ Session::enable_dump (std::string arg) { options.enable_dump_option (CompileOptions::HIR_DUMP_PRETTY); } + else if (arg == "bir") + { + options.enable_dump_option (CompileOptions::BIR_DUMP); + } else { rust_error_at ( @@ -659,6 +664,13 @@ Session::compile_crate (const char *filename) HIR::ConstChecker ().go (hir); + if (last_step == CompileOptions::CompileStep::BorrowCheck) + return; + + const bool dump_bir + = options.dump_option_enabled (CompileOptions::DumpOption::BIR_DUMP); + HIR::BorrowChecker (dump_bir).go (hir); + if (saw_errors ()) return; diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h index 7e82291f95f9..2b1f6d00d9c9 100644 --- a/gcc/rust/rust-session-manager.h +++ b/gcc/rust/rust-session-manager.h @@ -221,6 +221,7 @@ struct CompileOptions TARGET_OPTION_DUMP, HIR_DUMP, HIR_DUMP_PRETTY, + BIR_DUMP, }; std::set dump_options; @@ -254,6 +255,7 @@ struct CompileOptions Privacy, Unsafety, Const, + BorrowCheck, Compilation, End, } compile_until @@ -277,6 +279,7 @@ struct CompileOptions enable_dump_option (DumpOption::TARGET_OPTION_DUMP); enable_dump_option (DumpOption::HIR_DUMP); enable_dump_option (DumpOption::HIR_DUMP_PRETTY); + enable_dump_option (DumpOption::BIR_DUMP); } void set_crate_name (std::string name) From 9f20320e3bdbb5f40a47ba82a35c548470cc8d5d Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Thu, 19 Oct 2023 11:37:41 +0200 Subject: [PATCH 03/16] borrowck: Execute only with CLI flag gcc/rust/ChangeLog: * lang.opt: CLI flag. * rust-session-manager.cc (Session::compile_crate): Guard execution. Signed-off-by: Jakub Dupak --- gcc/rust/lang.opt | 4 ++++ gcc/rust/rust-session-manager.cc | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index 67285a6d92b2..83db5ec1711d 100644 --- a/gcc/rust/lang.opt +++ b/gcc/rust/lang.opt @@ -202,4 +202,8 @@ frust-name-resolution-2.0 Rust Var(flag_name_resolution_2_0) Use the temporary and experimental name resolution pipeline instead of the stable one +frust-borrowcheck +Rust Var(flag_borrowcheck) +Use the WIP borrow checker. + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index 2843b7d25cf2..96d5f4729165 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -667,9 +667,12 @@ Session::compile_crate (const char *filename) if (last_step == CompileOptions::CompileStep::BorrowCheck) return; - const bool dump_bir - = options.dump_option_enabled (CompileOptions::DumpOption::BIR_DUMP); - HIR::BorrowChecker (dump_bir).go (hir); + if (flag_borrowcheck) + { + const bool dump_bir + = options.dump_option_enabled (CompileOptions::DumpOption::BIR_DUMP); + HIR::BorrowChecker (dump_bir).go (hir); + } if (saw_errors ()) return; From d7bcc5e5262bf5a47687df3ffa97598e6a2a4919 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 19:49:59 +0200 Subject: [PATCH 04/16] borrowck: Create Borrow-checker IR (BIR) gcc/rust/ChangeLog: * checks/errors/borrowck/rust-borrow-checker.cc: Include to compile new code. * checks/errors/borrowck/rust-bir-place.h: New file. * checks/errors/borrowck/rust-bir-visitor.h: New file. * checks/errors/borrowck/rust-bir.h: New file. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-bir-place.h | 257 ++++++++++++++++++ .../checks/errors/borrowck/rust-bir-visitor.h | 62 +++++ gcc/rust/checks/errors/borrowck/rust-bir.h | 200 ++++++++++++++ .../errors/borrowck/rust-borrow-checker.cc | 2 + 4 files changed, 521 insertions(+) create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-place.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-visitor.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir.h diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-place.h b/gcc/rust/checks/errors/borrowck/rust-bir-place.h new file mode 100644 index 000000000000..ce32f9262ceb --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-place.h @@ -0,0 +1,257 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_PLACE_H +#define RUST_BIR_PLACE_H + +#include +#include "rust-mapping-common.h" +#include "rust-system.h" +#include "rust-tyty.h" + +namespace Rust { +namespace BIR { + +/** A unique identifier for a place in the BIR. */ +using PlaceId = uint32_t; + +static constexpr PlaceId INVALID_PLACE = 0; +static constexpr PlaceId RETURN_VALUE_PLACE = 1; +static constexpr PlaceId FIRST_VARIABLE_PLACE = RETURN_VALUE_PLACE; + +/** + * A unique identifier for a lifetime in the BIR. Only to be used INTERNALLY. + */ +using LifetimeID = uint32_t; + +constexpr LifetimeID INVALID_LIFETIME_ID = 0; +constexpr LifetimeID STATIC_LIFETIME_ID = 1; +constexpr LifetimeID FIRST_NORMAL_LIFETIME_ID = 2; + +/** Representation of lifetimes in BIR. */ +struct Lifetime +{ + LifetimeID id = INVALID_LIFETIME_ID; + + constexpr Lifetime (LifetimeID id) : id (id) {} + constexpr Lifetime (const Lifetime &) = default; + WARN_UNUSED_RESULT bool has_lifetime () const + { + return id != INVALID_LIFETIME_ID; + } + LifetimeID operator() () const { return id; } +}; +constexpr Lifetime NO_LIFETIME = {INVALID_LIFETIME_ID}; +constexpr Lifetime STATIC_LIFETIME = {STATIC_LIFETIME_ID}; + +/** + * Representation of lvalues and constants in BIR. + * See bir bir design notes (in this directory) and the Polonius book. + */ +struct Place +{ + enum Kind + { + INVALID, + VARIABLE, + TEMPORARY, + CONSTANT, + FIELD, + INDEX, + DEREF, + }; + + Kind kind; + uint32_t variable_or_field_index; // NodeId for VARIABLE + /** Data for traversing paths in the PlaceDB. */ + struct Path + { + PlaceId parent = INVALID_PLACE; + PlaceId first_child = INVALID_PLACE; + PlaceId next_sibling = INVALID_PLACE; + + Path (PlaceId parent, PlaceId first_child, PlaceId next_sibling) + : parent (parent), first_child (first_child), next_sibling (next_sibling) + {} + Path () = default; + } path; + /** Copy trait */ + bool is_copy; + /** This place can be moved from safety. */ + bool is_rvalue; + Lifetime lifetime; + TyTy::BaseType *tyty; + + Place (Kind kind, uint32_t variable_or_field_index, const Path &path, + bool is_copy, bool is_rvalue, const Lifetime &lifetime, + TyTy::BaseType *tyty) + : kind (kind), variable_or_field_index (variable_or_field_index), + path (path), is_copy (is_copy), is_rvalue (is_rvalue), + lifetime (lifetime), tyty (tyty) + {} +}; + +/** Allocated places and keeps track of paths. */ +class PlaceDB +{ + // Possible optimizations: separate variables to speedup lookup. + std::vector places; + std::unordered_map constants_lookup; + +public: + PlaceDB () + { + // Reserved index for invalid place. + places.push_back ( + {Place::INVALID, 0, {}, false, false, NO_LIFETIME, nullptr}); + } + + Place &operator[] (PlaceId id) { return places.at (id); } + const Place &operator[] (PlaceId id) const { return places.at (id); } + + size_t size () const { return places.size (); } + + PlaceId add_place (Place place, PlaceId last_sibling = 0) + { + places.push_back (place); + PlaceId new_place = places.size () - 1; + if (last_sibling == 0) + { + places[place.path.parent].path.first_child = new_place; + } + else + { + places[last_sibling].path.next_sibling = new_place; + } + return new_place; + } + + PlaceId add_variable (NodeId id, TyTy::BaseType *tyty) + { + return add_place ( + {Place::VARIABLE, id, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty}, + 0); + } + + PlaceId lookup_variable (NodeId id) + { + PlaceId current = FIRST_VARIABLE_PLACE; + + while (current != places.size ()) + { + if (places[current].kind == Place::VARIABLE + && places[current].variable_or_field_index == id) + return current; + current++; + } + return INVALID_PLACE; + }; + + PlaceId add_temporary (TyTy::BaseType *tyty) + { + return add_place ( + {Place::TEMPORARY, 0, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty}, + 0); + } + + WARN_UNUSED_RESULT PlaceId lookup_or_add_path (Place::Kind kind, + TyTy::BaseType *tyty, + PlaceId parent, size_t id = 0) + { + PlaceId current = 0; + if (parent < places.size ()) + { + current = places[parent].path.first_child; + while (current != 0) + { + if (places[current].kind == kind + && places[current].variable_or_field_index == id) + { + rust_assert (places[current].tyty->is_equal (*tyty)); + return current; + } + current = places[current].path.next_sibling; + } + } + return add_place ( + {kind, id, {parent, 0, 0}, is_type_copy (tyty), false, NO_LIFETIME, tyty}, + current); + } + + PlaceId get_constant (TyTy::BaseType *tyty) + { + auto lookup = constants_lookup.find (tyty); + if (lookup != constants_lookup.end ()) + return lookup->second; + Lifetime lifetime + = tyty->get_kind () == TyTy::REF ? STATIC_LIFETIME : NO_LIFETIME; + Place place + = {Place::CONSTANT, 0, {}, is_type_copy (tyty), false, lifetime, tyty}; + places.push_back (place); + return places.size () - 1; + } + +private: + static bool is_type_copy (TyTy::BaseType *ty) + { + switch (ty->get_kind ()) + { + case TyTy::REF: + case TyTy::POINTER: + case TyTy::SLICE: + case TyTy::BOOL: + case TyTy::CHAR: + case TyTy::INT: + case TyTy::UINT: + case TyTy::FLOAT: + case TyTy::USIZE: + case TyTy::ISIZE: + case TyTy::FNPTR: + case TyTy::FNDEF: + case TyTy::NEVER: + return true; + case TyTy::TUPLE: { + auto &fields = ty->as ()->get_fields (); + return std::all_of (fields.begin (), fields.end (), + [] (const TyTy::TyVar &field) { + return is_type_copy (field.get_tyty ()); + }); + } + case TyTy::ARRAY: { + return is_type_copy (ty->as ()->get_element_type ()); + } + case TyTy::INFER: + case TyTy::PARAM: + case TyTy::ERROR: + case TyTy::STR: + case TyTy::PLACEHOLDER: + rust_unreachable (); + case TyTy::ADT: // TODO: check trait + case TyTy::PROJECTION: // TODO: DUNNO + case TyTy::CLOSURE: // TODO: DUNNO + case TyTy::DYNAMIC: // TODO: dunno + return false; + } + rust_unreachable (); + } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_PLACE_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-visitor.h b/gcc/rust/checks/errors/borrowck/rust-bir-visitor.h new file mode 100644 index 000000000000..48b67c0fead4 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-visitor.h @@ -0,0 +1,62 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_VISITOR_H +#define RUST_BIR_VISITOR_H + +namespace Rust { +namespace BIR { + +class Node; +class InitializerExpr; +template class Operator; +class Assignment; +class BorrowExpr; +class CallExpr; + +class Visitor +{ +public: + virtual void visit (Node &node) = 0; + virtual void visit (InitializerExpr &expr) = 0; + virtual void visit (Operator<1> &expr) = 0; + virtual void visit (Operator<2> &expr) = 0; + virtual void visit (BorrowExpr &expr) = 0; + virtual void visit (Assignment &expr) = 0; + virtual void visit (CallExpr &expr) = 0; +}; + +class Visitable +{ +public: + virtual void accept_vis (Visitor &visitor) = 0; +}; + +template class VisitableImpl : public BASE +{ +public: + void accept_vis (Visitor &visitor) override + { + visitor.visit (static_cast (*this)); + } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_VISITOR_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir.h b/gcc/rust/checks/errors/borrowck/rust-bir.h new file mode 100644 index 000000000000..bcb32c9659b8 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir.h @@ -0,0 +1,200 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BASE_H +#define RUST_BIR_BASE_H + +#include "rust-bir-place.h" +#include "rust-bir-visitor.h" + +namespace Rust { + +namespace BIR { + +struct BasicBlock; +class Node; +class AbstractExpr; + +/** + * Top-level entity of the Borrow-checker IR (BIR). + * It represents a single function (method, closure, etc.), which is also the + * basic unit of Polonius borrow-checking. + */ +struct Function +{ + PlaceDB place_db; + std::vector arguments; // Only used for dump. + std::vector basic_blocks; +}; + +/** Single "instruction" of BIR. */ +class Node +{ +public: + enum class Kind + { + ASSIGNMENT, // = + SWITCH, // switch + RETURN, // return + GOTO, // goto + STORAGE_DEAD, // StorageDead() + STORAGE_LIVE, // StorageLive() + }; + +private: + Kind kind; + // ASSIGNMENT: lhs + // SWITCH: switch_val + // StorageDead/StorageLive: place + // otherwise: + PlaceId place; + // ASSIGNMENT: rhs + // otherwise: + std::unique_ptr expr; + +public: + Node (PlaceId lhs, AbstractExpr *rhs) + : kind (Kind::ASSIGNMENT), place (lhs), expr (rhs) + {} + + explicit Node (Kind kind, PlaceId place = INVALID_PLACE, + AbstractExpr *expr = nullptr) + : kind (kind), place (place), expr (expr) + {} + +public: + WARN_UNUSED_RESULT Kind get_kind () const { return kind; } + WARN_UNUSED_RESULT PlaceId get_place () const { return place; } + WARN_UNUSED_RESULT AbstractExpr &get_expr () const { return *expr; } +}; + +/** Unique identifier for a basic block in the BIR. */ +using BasicBlockId = uint32_t; + +static constexpr BasicBlockId INVALID_BB + = std::numeric_limits::max (); + +struct BasicBlock +{ + // BIR "instructions". + std::vector statements; + // A basic block can end with: goto, return or switch + std::vector successors; + +public: + WARN_UNUSED_RESULT bool is_terminated () const + { + if (statements.empty ()) + return false; + switch (statements.back ().get_kind ()) + { + case Node::Kind::GOTO: + case Node::Kind::RETURN: + case Node::Kind::SWITCH: + return true; + default: + return false; + } + } + + WARN_UNUSED_RESULT bool is_goto_terminated () const + { + return is_terminated () + && statements.back ().get_kind () == Node::Kind::GOTO; + } +}; + +// Rhs expression of BIR assignment node (abstract). +class AbstractExpr : public Visitable +{ +}; + +class InitializerExpr : public VisitableImpl +{ + std::vector values; + +public: + explicit InitializerExpr (std::vector &&values) : values (values) {} + +public: + std::vector &get_values () { return values; } +}; + +template +class Operator : public VisitableImpl> +{ + std::array operands; + +public: + explicit Operator (std::array &&operands) + : operands (operands) + {} + +public: + template WARN_UNUSED_RESULT PlaceId get_operand () const + { + static_assert (I < ARITY, "Index out of bounds"); + return operands[I]; + } +}; + +class BorrowExpr : public VisitableImpl +{ + PlaceId place; + +public: + explicit BorrowExpr (PlaceId place) : place (place) {} + WARN_UNUSED_RESULT PlaceId get_place () const { return place; } +}; + +/** + * This expression is only to be used inside the assignment node and acts as + * identity wrapper for a place value. It is separated from `Operator<1>` to + * render it more explicitly in the dump. + */ +class Assignment : public VisitableImpl +{ + PlaceId rhs; + +public: + explicit Assignment (PlaceId rhs) : rhs (rhs) {} + +public: + WARN_UNUSED_RESULT PlaceId get_rhs () const { return rhs; } +}; + +class CallExpr : public VisitableImpl +{ + std::vector arguments; + PlaceId callable; + +public: + explicit CallExpr (PlaceId callable, std::vector &&arguments) + : arguments (arguments), callable (callable) + {} + +public: + const std::vector &get_arguments () { return arguments; } + WARN_UNUSED_RESULT PlaceId get_callable () const { return callable; } +}; + +} // namespace BIR + +} // namespace Rust + +#endif // RUST_BIR_BASE_H diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index 6c2922310423..44d33c57838a 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -18,6 +18,8 @@ #include "rust-borrow-checker.h" #include "rust-function-collector.h" +#include "rust-bir.h" +#include "rust-bir-visitor.h" namespace Rust { namespace HIR { From 46564756cb0cb02c0a95f10cc14033bc4009562c Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 20:43:17 +0200 Subject: [PATCH 05/16] borrowck: Create BIR builders (visitors) gcc/rust/ChangeLog: * Make-lang.in: Compile BIR expr visitor. * checks/errors/borrowck/rust-borrow-checker.cc (BorrowChecker::go): Use BIR builder. * rust-session-manager.cc (Session::compile_crate): Run borrow checker. * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc: New file. * checks/errors/borrowck/rust-bir-builder-expr-stmt.h: New file. * checks/errors/borrowck/rust-bir-builder-internal.h: New file. * checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h: New file. * checks/errors/borrowck/rust-bir-builder-pattern.h: New file. * checks/errors/borrowck/rust-bir-builder-struct.h: New file. * checks/errors/borrowck/rust-bir-builder.h: New file. Signed-off-by: Jakub Dupak --- gcc/rust/Make-lang.in | 1 + .../borrowck/rust-bir-builder-expr-stmt.cc | 578 ++++++++++++++++++ .../borrowck/rust-bir-builder-expr-stmt.h | 150 +++++ .../borrowck/rust-bir-builder-internal.h | 416 +++++++++++++ .../borrowck/rust-bir-builder-lazyboolexpr.h | 243 ++++++++ .../borrowck/rust-bir-builder-pattern.h | 290 +++++++++ .../errors/borrowck/rust-bir-builder-struct.h | 270 ++++++++ .../checks/errors/borrowck/rust-bir-builder.h | 88 +++ .../errors/borrowck/rust-borrow-checker.cc | 8 +- gcc/rust/rust-session-manager.cc | 2 +- 10 files changed, 2042 insertions(+), 4 deletions(-) create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-builder.h diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index f4b9eb5502e2..1e462ef6a1bf 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -149,6 +149,7 @@ GRS_OBJS = \ rust/rust-hir-type-check-enumitem.o \ rust/rust-hir-type-check-implitem.o \ rust/rust-borrow-checker.o \ + rust/rust-bir-builder-expr-stmt.o \ rust/rust-hir-dot-operator.o \ rust/rust-hir-path-probe.o \ rust/rust-type-util.o \ diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc new file mode 100644 index 000000000000..1487c853b49e --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -0,0 +1,578 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If +// not see +// . + +#include "rust-bir-builder-expr-stmt.h" +#include "rust-bir-builder-lazyboolexpr.h" +#include "rust-bir-builder-pattern.h" +#include "rust-bir-builder-struct.h" + +namespace Rust { +namespace BIR { + +void +ExprStmtBuilder::visit (HIR::ClosureExpr &expr) +{ + auto closure_ty = lookup_type (expr)->as (); + std::vector captures; + for (auto &capture : closure_ty->get_captures ()) + { + captures.push_back (ctx.place_db.lookup_variable (capture)); + } + + // Not a coercion site. + return_expr (new InitializerExpr (std::move (captures)), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::StructExprStructFields &fields) +{ + auto struct_ty + = lookup_type (fields)->as ()->get_variants ().at (0); + auto init_values = StructBuilder (ctx, struct_ty).build (fields); + return_expr (new InitializerExpr (std::move (init_values)), + lookup_type (fields)); +} + +void +ExprStmtBuilder::visit (HIR::StructExprStruct &expr) +{ + // There is no way to modify empty struct, which makes them constant. + return_place (ctx.place_db.get_constant (lookup_type (expr))); +} + +void +ExprStmtBuilder::visit (HIR::LiteralExpr &expr) +{ + // Different literal values of the same type are not distinguished. + return_place (ctx.place_db.get_constant (lookup_type (expr))); +} + +void +ExprStmtBuilder::visit (HIR::BorrowExpr &expr) +{ + auto operand = visit_expr (*expr.get_expr ()); + return_expr (new BorrowExpr (operand), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::DereferenceExpr &expr) +{ + auto operand = visit_expr (*expr.get_expr ()); + return_place (operand); +} + +void +ExprStmtBuilder::visit (HIR::ErrorPropagationExpr &expr) +{ + rust_sorry_at (expr.get_locus (), "error propagation is not supported"); +} + +void +ExprStmtBuilder::visit (HIR::NegationExpr &expr) +{ + PlaceId operand = visit_expr (*expr.get_expr ()); + return_expr (new Operator<1> ({operand}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::ArithmeticOrLogicalExpr &expr) +{ + PlaceId lhs = visit_expr (*expr.get_lhs ()); + PlaceId rhs = visit_expr (*expr.get_rhs ()); + return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::ComparisonExpr &expr) +{ + PlaceId lhs = visit_expr (*expr.get_lhs ()); + PlaceId rhs = visit_expr (*expr.get_rhs ()); + return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::LazyBooleanExpr &expr) +{ + return_place (LazyBooleanExprBuilder (ctx).build (expr)); +} + +void +ExprStmtBuilder::visit (HIR::TypeCastExpr &expr) +{ + return_place (visit_expr (*expr.get_expr ())); +} + +void +ExprStmtBuilder::visit (HIR::AssignmentExpr &expr) +{ + auto lhs = visit_expr (*expr.get_lhs ()); + auto rhs = visit_expr (*expr.get_rhs ()); + push_assignment (lhs, rhs); +} + +void +ExprStmtBuilder::visit (HIR::CompoundAssignmentExpr &expr) +{ + auto lhs = visit_expr (*expr.get_lhs ()); + auto rhs = visit_expr (*expr.get_rhs ()); + push_assignment (lhs, new Operator<2> ({lhs, rhs})); + // TODO: (philip) nicer unit? + return_place (ctx.place_db.get_constant (lookup_type (expr))); +} + +void +ExprStmtBuilder::visit (HIR::GroupedExpr &expr) +{ + // Uses accept_vis directly to avoid creating n new temporary. + expr.get_expr_in_parens ()->accept_vis (*this); +} + +void +ExprStmtBuilder::visit (HIR::ArrayExpr &expr) +{ + switch (expr.get_internal_elements ()->get_array_expr_type ()) + { + case HIR::ArrayElems::VALUES: { + auto init_values = visit_list ((static_cast ( + expr.get_internal_elements ().get ())) + ->get_values ()); + return_expr (new InitializerExpr (std::move (init_values)), + lookup_type (expr)); + break; + } + case HIR::ArrayElems::COPIED: { + auto init = visit_expr (*(static_cast ( + expr.get_internal_elements ().get ())) + ->get_elem_to_copy ()); + return_expr (new InitializerExpr ({init}), lookup_type (expr)); + break; + } + } +} + +void +ExprStmtBuilder::visit (HIR::ArrayIndexExpr &expr) +{ + auto lhs = visit_expr (*expr.get_array_expr ()); + auto rhs = visit_expr (*expr.get_index_expr ()); + // The Index is not tracked in BIR. + (void) rhs; + return_place ( + ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (expr), lhs)); +} + +void +ExprStmtBuilder::visit (HIR::TupleExpr &expr) +{ + std::vector init_values = visit_list (expr.get_tuple_elems ()); + if (std::any_of (init_values.begin (), init_values.end (), + [this] (PlaceId id) { + return ctx.place_db[id].lifetime.has_lifetime (); + })) + { + ctx.place_db[expr_return_place].lifetime + = {ctx.lifetime_interner.get_anonymous ()}; + } + return_expr (new InitializerExpr (std::move (init_values)), + lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::TupleIndexExpr &expr) +{ + auto tuple = visit_expr (*expr.get_tuple_expr ()); + return_place (ctx.place_db.lookup_or_add_path (Place::FIELD, + lookup_type (expr), tuple, + expr.get_tuple_index ())); +} + +void +ExprStmtBuilder::visit (HIR::CallExpr &expr) +{ + PlaceId fn = visit_expr (*expr.get_fnexpr ()); + std::vector arguments = visit_list (expr.get_arguments ()); + + TyTy::BaseType *call_type = ctx.place_db[fn].tyty; + if (auto fn_type = call_type->try_as ()) + { + for (size_t i = 0; i < fn_type->get_params ().size (); ++i) + { + coercion_site (arguments[i], fn_type->get_params ()[i].second); + } + } + else if (auto fn_ptr_type = call_type->try_as ()) + { + for (size_t i = 0; i < fn_ptr_type->get_params ().size (); ++i) + { + coercion_site (arguments[i], + fn_ptr_type->get_params ()[i].get_tyty ()); + } + } + else + { + rust_unreachable (); + } + + return_expr (new CallExpr (fn, std::move (arguments)), lookup_type (expr), + true); +} + +void +ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) +{ + auto receiver = visit_expr (*expr.get_receiver_expr ()); + auto type = autoderef (receiver); + rust_assert (type->get_kind () == TyTy::ADT); + auto adt = type->as (); + rust_assert (!adt->is_enum ()); + rust_assert (adt->number_of_variants () == 1); + TyTy::VariantDef *variant = adt->get_variants ().at (0); + + TyTy::StructFieldType *field_ty = nullptr; + size_t field_index = 0; + bool ok = variant->lookup_field (expr.get_field_name ().as_string (), + &field_ty, &field_index); + rust_assert (ok); + + return_place (ctx.place_db.lookup_or_add_path (Place::FIELD, + field_ty->get_field_type (), + receiver, field_index)); +} + +void +ExprStmtBuilder::visit (HIR::BlockExpr &block) +{ + for (auto &stmt : block.get_statements ()) + { + stmt->accept_vis (*this); + } + if (block.has_expr ()) + { + return_place (visit_expr (*block.get_final_expr ())); + } +} + +void +ExprStmtBuilder::visit (HIR::ContinueExpr &cont) +{ + // BuilderContext::LabelledBlockCtx loop_ctx; + // NodeId label = UNKNOWN_NODEID; + // if (cont.has_label ()) + // { + // if (!resolve_label (cont.get_label (), label)) + // return; + // } + // + // if (!find_block_ctx (label, loop_ctx)) + // { + // rust_error_at (cont.get_locus (), "unresolved loop label"); + // } + // + // add_jump_to (loop_ctx.continue_bb); +} + +void +ExprStmtBuilder::visit (HIR::BreakExpr &brk) +{ + // BuilderContext::LabelledBlockCtx block_ctx{}; + // NodeId label = UNKNOWN_NODEID; + // if (brk.has_label ()) + // { + // if (!resolve_label (brk.get_label (), label)) + // return; + // } + // if (!find_block_ctx (label, block_ctx)) + // { + // rust_error_at (brk.get_locus (), "unresolved labelled block"); + // } + // + // if (brk.has_break_expr ()) + // { + // brk.get_expr ()->accept_vis (*this); + // push_assignment (block_ctx.label_var, new Operator<1> ({translated})); + // } + // + // add_jump_to (block_ctx.break_bb); +} + +void +ExprStmtBuilder::visit (HIR::RangeFromToExpr &range) +{ + auto from = visit_expr (*range.get_from_expr ()); + auto to = visit_expr (*range.get_to_expr ()); + return_expr (new InitializerExpr ({from, to}), lookup_type (range)); +} + +void +ExprStmtBuilder::visit (HIR::RangeFromExpr &expr) +{ + auto from = visit_expr (*expr.get_from_expr ()); + return_expr (new InitializerExpr ({from}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::RangeToExpr &expr) +{ + auto to = visit_expr (*expr.get_to_expr ()); + return_expr (new InitializerExpr ({to}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::RangeFullExpr &expr) +{ + return_expr (new InitializerExpr ({}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::RangeFromToInclExpr &expr) +{ + auto from = visit_expr (*expr.get_from_expr ()); + auto to = visit_expr (*expr.get_to_expr ()); + return_expr (new InitializerExpr ({from, to}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::RangeToInclExpr &expr) +{ + auto to = visit_expr (*expr.get_to_expr ()); + return_expr (new InitializerExpr ({to}), lookup_type (expr)); +} + +void +ExprStmtBuilder::visit (HIR::ReturnExpr &ret) +{ + if (ret.has_return_expr ()) + { + push_assignment (RETURN_VALUE_PLACE, visit_expr (*ret.get_expr ())); + } + ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN); +} + +void +ExprStmtBuilder::visit (HIR::UnsafeBlockExpr &expr) +{ + rust_sorry_at (expr.get_locus (), "unsafe blocks are not supported"); +} + +void +ExprStmtBuilder::visit (HIR::LoopExpr &expr) +{ + // PlaceId label_var = ctx.place_db.add_temporary (nullptr); + // NodeId label; + // if (!resolve_label (expr.get_loop_label (), label)) + // return; + // ctx.label_place_map.emplace (label, label_var); + // + // expr.get_loop_block ()->accept_vis (*this); + // + // translated = label_var; +} +void +ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) +{ + // // TODO: Desugar in AST->HIR ??? + // PlaceId label_var = ctx.place_db.add_temporary (nullptr); + // NodeId label; + // if (!resolve_label (expr.get_loop_label (), label)) + // return; + // ctx.label_place_map.emplace (label, label_var); + // + // expr.get_predicate_expr ()->accept_vis (*this); + // + // expr.get_loop_block ()->accept_vis (*this); + // + // translated = label_var; +} +void +ExprStmtBuilder::visit (HIR::WhileLetLoopExpr &expr) +{ + // TODO: Desugar in AST->HIR +} +void +ExprStmtBuilder::visit (HIR::IfExpr &expr) +{ + // If without else cannot return a non-unit value (see [E0317]). + + push_switch (visit_expr (*expr.get_if_condition ())); + BasicBlockId if_block = ctx.current_bb; + + ctx.current_bb = new_bb (); + (void) visit_expr (*expr.get_if_block ()); + BasicBlockId then_block = ctx.current_bb; + + ctx.current_bb = new_bb (); + BasicBlockId final_block = ctx.current_bb; + return_unit (expr); + + // Jumps are added at the end to match rustc MIR order for easier comparison. + add_jump (if_block, then_block); + add_jump (if_block, final_block); + add_jump (then_block, final_block); +} + +void +ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) +{ + PlaceId result = ctx.place_db.add_temporary (lookup_type (expr)); + + push_switch (visit_expr (*expr.get_if_condition ())); + BasicBlockId if_block = ctx.current_bb; + + ctx.current_bb = new_bb (); + auto then_res = visit_expr (*expr.get_if_block ()); + push_assignment (result, then_res); + BasicBlockId then_block = ctx.current_bb; + + ctx.current_bb = new_bb (); + auto else_res = visit_expr (*expr.get_else_block ()); + push_assignment (result, else_res); + BasicBlockId else_block = ctx.current_bb; + + ctx.current_bb = new_bb (); + BasicBlockId final_block = ctx.current_bb; + return_place (result); + + // Jumps are added at the end to match rustc MIR order for easier comparison. + add_jump (if_block, then_block); + add_jump (if_block, else_block); + add_jump (then_block, final_block); + add_jump (else_block, final_block); +} +void +ExprStmtBuilder::visit (HIR::IfLetExpr &expr) +{ + rust_sorry_at (expr.get_locus (), "if let expressions are not supported"); +} +void +ExprStmtBuilder::visit (HIR::IfLetExprConseqElse &expr) +{ + rust_sorry_at (expr.get_locus (), "if let expressions are not supported"); +} +void +ExprStmtBuilder::visit (HIR::MatchExpr &expr) +{ + // // TODO + // expr.get_scrutinee_expr ()->accept_vis (*this); + // PlaceId scrutinee = translated; + // + // BasicBlockId final_bb = new_bb (); + // + // BasicBlockId next_case_bb = new_bb (); + // for (auto &match_case : expr.get_match_cases ()) + // { + // BasicBlockId body_bb = new_bb (); + // + // BasicBlockId next_pattern_bb = new_bb (); + // for (auto &pat : match_case.get_arm ().get_patterns ()) + // { + // compile_pattern_validation (*pat, scrutinee); + // push_switch (translated); + // add_jump_to (next_pattern_bb); + // start_new_subsequent_bb (); + // compile_pattern_bindings (*pat, scrutinee); + // add_jump_to (body_bb); + // + // ctx.current_bb = next_pattern_bb; + // next_pattern_bb = new_bb (); + // } + // ctx.current_bb = next_pattern_bb; + // // No pattern matched, go to the next case. + // add_jump_to (next_case_bb); + // + // ctx.current_bb = body_bb; + // match_case.get_expr ()->accept_vis (*this); + // add_jump_to (final_bb); + // + // ctx.current_bb = next_case_bb; + // next_case_bb = new_bb (); + // } + // add_jump_to (final_bb); + // + // ctx.current_bb = final_bb; +} + +void +ExprStmtBuilder::visit (HIR::AwaitExpr &expr) +{ + rust_sorry_at (expr.get_locus (), "await expressions are not supported"); +} + +void +ExprStmtBuilder::visit (HIR::AsyncBlockExpr &expr) +{ + rust_sorry_at (expr.get_locus (), "async blocks are not supported"); +} + +void +ExprStmtBuilder::visit (HIR::QualifiedPathInExpression &expr) +{ + PlaceId result; + // Note: Type is only stored for the expr, not the segment. + bool ok = resolve_variable (expr.get_final_segment (), result); + rust_assert (ok); + return_place (result); +} + +void +ExprStmtBuilder::visit (HIR::PathInExpression &expr) +{ + PlaceId result; + // Note: Type is only stored for the expr, not the segment. + bool ok = resolve_variable (expr.get_final_segment (), result); + rust_assert (ok); + return_place (result); +} + +void +ExprStmtBuilder::visit (HIR::LetStmt &stmt) +{ + if (stmt.has_init_expr ()) + { + auto init = visit_expr (*stmt.get_init_expr ()); + PatternBindingBuilder (ctx, init, stmt.get_type ().get ()) + .go (*stmt.get_pattern ()); + } + else if (stmt.get_pattern ()->get_pattern_type () == HIR::Pattern::IDENTIFIER) + { + auto var = declare_variable (stmt.get_pattern ()->get_mappings ()); + auto &var_place = ctx.place_db[var]; + if (var_place.tyty->get_kind () == TyTy::REF) + { + auto p_type = tl::optional ( + static_cast (stmt.get_type ().get ())); + var_place.lifetime = ctx.lookup_lifetime ( + p_type.map (&HIR::ReferenceType::get_lifetime)); + } + return; + } + else + { + // TODO + rust_sorry_at (stmt.get_locus (), "pattern matching in let statements"); + } +} + +void +ExprStmtBuilder::visit (HIR::ExprStmt &stmt) +{ + (void) visit_expr (*stmt.get_expr ()); +} + +} // namespace BIR +} // namespace Rust \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h new file mode 100644 index 000000000000..f46cba5f9684 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h @@ -0,0 +1,150 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_EXPR_H +#define RUST_BIR_BUILDER_EXPR_H + +#include "rust-hir-visitor.h" +#include "rust-bir-builder-internal.h" + +namespace Rust { +namespace BIR { + +class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor +{ + PlaceId expr_return_place = INVALID_PLACE; + +public: + explicit ExprStmtBuilder (BuilderContext &ctx) : AbstractExprBuilder (ctx) {} + + PlaceId build (HIR::Expr &expr) { return visit_expr (expr); } + +private: + template + std::vector visit_list (std::vector> &list) + { + std::vector result; + for (auto &elem : list) + { + result.push_back (visit_expr (*elem)); + } + return result; + } + +protected: // Expr + // TODO: test when compiles + void visit (HIR::ClosureExpr &expr) override; + void visit (HIR::StructExprStructFields &fields) override; + void visit (HIR::StructExprStruct &expr) override; + void visit (HIR::LiteralExpr &expr) override; + void visit (HIR::BorrowExpr &expr) override; + void visit (HIR::DereferenceExpr &expr) override; + // TODO: desugar in AST->HIR + void visit (HIR::ErrorPropagationExpr &expr) override; + void visit (HIR::NegationExpr &expr) override; + void visit (HIR::ArithmeticOrLogicalExpr &expr) override; + void visit (HIR::ComparisonExpr &expr) override; + void visit (HIR::LazyBooleanExpr &expr) override; + void visit (HIR::TypeCastExpr &expr) override; + void visit (HIR::AssignmentExpr &expr) override; + void visit (HIR::CompoundAssignmentExpr &expr) override; + void visit (HIR::GroupedExpr &expr) override; + void visit (HIR::ArrayExpr &expr) override; + void visit (HIR::ArrayIndexExpr &expr) override; + void visit (HIR::TupleExpr &expr) override; + void visit (HIR::TupleIndexExpr &expr) override; + void visit (HIR::CallExpr &expr) override; + void visit (HIR::MethodCallExpr &expr) override {} + void visit (HIR::FieldAccessExpr &expr) override; + void visit (HIR::BlockExpr &block) override; + void visit (HIR::ContinueExpr &cont) override; + void visit (HIR::BreakExpr &brk) override; + void visit (HIR::RangeFromToExpr &range) override; + void visit (HIR::RangeFromExpr &expr) override; + void visit (HIR::RangeToExpr &expr) override; + void visit (HIR::RangeFullExpr &expr) override; + void visit (HIR::RangeFromToInclExpr &expr) override; + void visit (HIR::RangeToInclExpr &expr) override; + void visit (HIR::ReturnExpr &ret) override; + void visit (HIR::UnsafeBlockExpr &expr) override; + void visit (HIR::LoopExpr &expr) override; + void visit (HIR::WhileLoopExpr &expr) override; + void visit (HIR::WhileLetLoopExpr &expr) override; + void visit (HIR::IfExpr &expr) override; + void visit (HIR::IfExprConseqElse &expr) override; + + void visit (HIR::IfLetExpr &expr) override; + void visit (HIR::IfLetExprConseqElse &expr) override; + void visit (HIR::MatchExpr &expr) override; + void visit (HIR::AwaitExpr &expr) override; + void visit (HIR::AsyncBlockExpr &expr) override; + + // Nodes not containing executable code. Nothing to do. + void visit (HIR::QualifiedPathInExpression &expr) override; + void visit (HIR::PathInExpression &expr) override; + +protected: // Handled by other visitors + void visit (HIR::StructExprFieldIdentifier &field) override + { + rust_unreachable (); + } + void visit (HIR::StructExprFieldIdentifierValue &field) override + { + rust_unreachable (); + } + void visit (HIR::StructExprFieldIndexValue &field) override + { + rust_unreachable (); + } + +protected: // Stmt + void visit (HIR::LetStmt &stmt) override; + + void visit (HIR::ExprStmt &stmt) override; + +protected: // Unused + // Only executable code of a single function/method is translated. + // All other items are ignored. + void visit (HIR::EnumItemTuple &tuple) override {} + void visit (HIR::EnumItemStruct &a_struct) override {} + void visit (HIR::EnumItem &item) override {} + void visit (HIR::TupleStruct &tuple_struct) override {} + void visit (HIR::EnumItemDiscriminant &discriminant) override {} + void visit (HIR::TypePathSegmentFunction &segment) override {} + void visit (HIR::TypePath &path) override {} + void visit (HIR::QualifiedPathInType &path) override {} + void visit (HIR::Module &module) override {} + void visit (HIR::ExternCrate &crate) override {} + void visit (HIR::UseDeclaration &use_decl) override {} + void visit (HIR::Function &function) override {} + void visit (HIR::TypeAlias &type_alias) override {} + void visit (HIR::StructStruct &struct_item) override {} + void visit (HIR::Enum &enum_item) override {} + void visit (HIR::Union &union_item) override {} + void visit (HIR::ConstantItem &const_item) override {} + void visit (HIR::StaticItem &static_item) override {} + void visit (HIR::Trait &trait) override {} + void visit (HIR::ImplBlock &impl) override {} + void visit (HIR::ExternBlock &block) override {} + void visit (HIR::EmptyStmt &stmt) override {} +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_EXPR_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h new file mode 100644 index 000000000000..48116d8c351c --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -0,0 +1,416 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_INTERNAL_H +#define RUST_BIR_BUILDER_INTERNAL_H + +#include "rust-bir-place.h" +#include "rust-hir-expr.h" +#include "rust-hir-item.h" +#include "rust-hir-type-check.h" +#include "rust-hir-visitor.h" +#include "rust-name-resolver.h" +#include "rust-bir.h" + +namespace Rust { + +namespace BIR { + +class LifetimeResolver +{ + using Index = uint32_t; + using Value = std::string; + + Index next_index = FIRST_NORMAL_LIFETIME_ID; + std::unordered_map value_to_index; + +public: + Index intern (const Value &value) + { + auto found = value_to_index.find (value); + if (found != value_to_index.end ()) + { + return found->second; + } + value_to_index.emplace (value, next_index); + return next_index++; + } + Index get_anonymous () { return next_index++; } +}; + +struct BuilderContext +{ + struct LabelledBlockCtx + { + NodeId label; // UNKNOWN_NODEID if no label + PlaceId label_var; + BasicBlockId break_bb; + BasicBlockId continue_bb; // Only valid for loops + }; + + // Context + Resolver::TypeCheckContext &tyctx; + Resolver::Resolver &resolver; + + std::vector basic_blocks; + size_t current_bb = 0; + + /** + * Allocation and lookup of places (variables, temporaries, paths, and + * constants) + */ + PlaceDB place_db; + LifetimeResolver lifetime_interner; + std::vector arguments; + /** + * Since labels can be used to return values, we need to reserve a place for + * them. This map associates labels with their respective places. + */ + std::unordered_map label_place_map; + + std::vector loop_stack; + +public: + BuilderContext () + : tyctx (*Resolver::TypeCheckContext::get ()), + resolver (*Resolver::Resolver::get ()) + { + basic_blocks.emplace_back (); // StartBB + } + + BasicBlock &get_current_bb () { return basic_blocks[current_bb]; } + + Lifetime lookup_lifetime (const tl::optional &lifetime) + { + if (!lifetime.has_value ()) + return {lifetime_interner.get_anonymous ()}; + switch (lifetime->get_lifetime_type ()) + { + case AST::Lifetime::NAMED: { + return {lifetime_interner.intern (lifetime->get_name ())}; + } + case AST::Lifetime::STATIC: { + return STATIC_LIFETIME; + } + case AST::Lifetime::WILDCARD: { + rust_sorry_at (lifetime->get_locus (), + "lifetime elision is not yet implemented"); + return NO_LIFETIME; + } + } + rust_unreachable (); + }; +}; + +// Common infrastructure for building BIR from HIR. +class AbstractBuilder +{ +protected: + BuilderContext &ctx; + + // This emulates the return value of the visitor, to be able to use the + // current visitor infrastructure, where the return value is forced to be + // void. + PlaceId translated = INVALID_PLACE; + +protected: + explicit AbstractBuilder (BuilderContext &ctx) : ctx (ctx) {} + + WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Expr &expr) + { + return expr.get_mappings ().get_nodeid (); + } + + WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Pattern &pattern) + { + return pattern.get_mappings ().get_nodeid (); + } + + template + WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (T &pattern) const + { + return lookup_type (pattern.get_mappings ().get_hirid ()); + } + + WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (HirId hirid) const + { + TyTy::BaseType *type = nullptr; + bool ok = ctx.tyctx.lookup_type (hirid, &type); + rust_assert (ok); + rust_assert (type != nullptr); + return type; + } + + PlaceId declare_variable (const Analysis::NodeMapping &node) + { + const NodeId nodeid = node.get_nodeid (); + const HirId hirid = node.get_hirid (); + + // In debug mode check that the variable is not already declared. + rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE); + + return ctx.place_db.add_variable (nodeid, lookup_type (hirid)); + } + + PlaceId declare_variable (const Analysis::NodeMapping &node, + TyTy::BaseType *type) + { + const NodeId nodeid = node.get_nodeid (); + + // In debug mode check that the variable is not already declared. + rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE); + + return ctx.place_db.add_variable (nodeid, type); + } + + void push_assignment (PlaceId lhs, AbstractExpr *rhs) + { + ctx.get_current_bb ().statements.emplace_back (lhs, rhs); + translated = lhs; + } + + void push_assignment (PlaceId lhs, PlaceId rhs) + { + push_assignment (lhs, new Assignment (rhs)); + } + + void push_tmp_assignment (AbstractExpr *rhs, TyTy::BaseType *tyty) + { + PlaceId tmp = ctx.place_db.add_temporary (tyty); + push_assignment (tmp, rhs); + } + + void push_switch (PlaceId switch_val) + { + ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, + switch_val); + } + + void push_storage_dead (PlaceId place) + { + ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_DEAD, + place); + } + + void push_storage_live (PlaceId place) + { + ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_LIVE, + place); + } + + BasicBlockId new_bb () + { + ctx.basic_blocks.emplace_back (); + return ctx.basic_blocks.size () - 1; + } + + BasicBlockId start_new_subsequent_bb () + { + BasicBlockId bb = new_bb (); + ctx.get_current_bb ().successors.emplace_back (bb); + ctx.current_bb = bb; + return bb; + } + + void add_jump (BasicBlockId from, BasicBlockId to) + { + ctx.basic_blocks[from].successors.emplace_back (to); + } + + void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); } + +protected: + template bool resolve_label (T &label, NodeId &resolved_label) + { + if (!ctx.resolver.lookup_resolved_label ( + label.get_mappings ().get_nodeid (), &resolved_label)) + { + rust_error_at (label.get_locus (), "unresolved label"); + return false; + } + return true; + } + + template + bool resolve_variable (T &variable, PlaceId &resolved_variable) + { + NodeId variable_id; + if (!ctx.resolver.lookup_resolved_name ( + variable.get_mappings ().get_nodeid (), &variable_id)) + { + // TODO: should this be assert? (should be caught by typecheck) + rust_error_at (variable.get_locus (), "unresolved variable"); + return false; + } + resolved_variable = ctx.place_db.lookup_variable (variable_id); + return true; + } + + bool find_block_ctx (NodeId label, BuilderContext::LabelledBlockCtx &block) + { + if (ctx.loop_stack.empty ()) + return false; + if (label == UNKNOWN_NODEID) + { + block = ctx.loop_stack.back (); + return true; + } + auto found + = std::find_if (ctx.loop_stack.rbegin (), ctx.loop_stack.rend (), + [&label] (const BuilderContext::LabelledBlockCtx &block) { + return block.label == label; + }); + if (found == ctx.loop_stack.rend ()) + return false; + block = *found; + return true; + } + + /** + * Performs implicit coercions on the `translated` place defined for a + * coercion site. + * + * Reference: https://doc.rust-lang.org/reference/type-coercions.html + * + * The only coercion relevant to BIR is the autoderef. All other coercions + * will be taken in account because the type is extracted from each node and + * not derived from operations in HIR/BIR. The borrowck does not care about + * type transitions. Lifetimes are not coerced, rather new are created with + * defined bounds to the original ones. + */ + void coercion_site (PlaceId &place, TyTy::BaseType *expected_ty) + { + auto count_ref_levels = [] (TyTy::BaseType *ty) { + size_t count = 0; + while (auto r = ty->try_as ()) + { + ty = r->get_base (); + count++; + } + return count; + }; + + auto actual_ty = ctx.place_db[place].tyty; + + auto deref_count + = count_ref_levels (actual_ty) - count_ref_levels (expected_ty); + + for (size_t i = 0; i < deref_count; ++i) + { + actual_ty = actual_ty->as ()->get_base (); + place + = ctx.place_db.lookup_or_add_path (Place::DEREF, actual_ty, place); + } + } + + /** Dereferences the `translated` place until it is at most one reference and + * return the base type. */ + TyTy::BaseType *autoderef (PlaceId &place) + { + auto ty = ctx.place_db[place].tyty; + while (auto ref_ty = ty->try_as ()) + { + ty = ref_ty->get_base (); + place = ctx.place_db.lookup_or_add_path (Place::DEREF, ty, place); + } + return ty; + } + + /** For operator */ + void autoref () + { + if (ctx.place_db[translated].tyty->get_kind () != TyTy::REF) + { + auto ty = ctx.place_db[translated].tyty; + push_tmp_assignment ( + new BorrowExpr (translated), + new TyTy::ReferenceType (ty->get_ref (), TyTy::TyVar (ty->get_ref ()), + Mutability::Imm)); + } + } +}; + +class AbstractExprBuilder : public AbstractBuilder, + public HIR::HIRExpressionVisitor +{ +protected: + // Exactly one of this and `translated` is used by each visitor. + AbstractExpr *expr_return_expr = nullptr; + +protected: + explicit AbstractExprBuilder (BuilderContext &ctx) : AbstractBuilder (ctx) {} + + PlaceId visit_expr (HIR::Expr &expr) + { + // Reset return places. + translated = INVALID_PLACE; + expr_return_expr = nullptr; + expr.accept_vis (*this); + if (translated != INVALID_PLACE) + { + auto result = translated; + translated = INVALID_PLACE; + return result; + } + else if (expr_return_expr != nullptr) + { + // Only allocate temporary, if needed. + push_tmp_assignment (expr_return_expr, lookup_type (expr)); + expr_return_expr = nullptr; + return translated; + } + else + { + return ctx.place_db.get_constant (lookup_type (expr)); + } + } + + void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, + bool can_panic = false) + { + rust_assert (expr_return_expr == nullptr); + if (can_panic) + { + push_tmp_assignment (expr, ty); + // TODO, cleanup? + start_new_subsequent_bb (); + } + else + { + translated = INVALID_PLACE; + expr_return_expr = expr; + } + } + + void return_place (PlaceId place) + { + expr_return_expr = nullptr; + translated = place; + } + + void return_unit (HIR::Expr &expr) + { + expr_return_expr = nullptr; + translated = ctx.place_db.get_constant (lookup_type (expr)); + } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_INTERNAL_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h new file mode 100644 index 000000000000..4ec87d9c71e6 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h @@ -0,0 +1,243 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_LAZYBOOLEXPR_H +#define RUST_BIR_BUILDER_LAZYBOOLEXPR_H + +#include "rust-bir-builder-internal.h" +#include "rust-bir-builder-expr-stmt.h" +#include "rust-hir-expr.h" + +namespace Rust { +namespace BIR { + +/** + * Special builder is needed to store short-circuiting context for directly + * nested lazy boolean expressions. + */ +class LazyBooleanExprBuilder : public AbstractBuilder, + public HIR::HIRExpressionVisitor +{ + BasicBlockId short_circuit_bb; + +public: + explicit LazyBooleanExprBuilder (BuilderContext &ctx) + : AbstractBuilder (ctx), short_circuit_bb (0) + {} + + PlaceId build (HIR::LazyBooleanExpr &expr) + { + PlaceId return_place = ctx.place_db.add_temporary (lookup_type (expr)); + + short_circuit_bb = new_bb (); + visit (expr); + push_assignment (return_place, translated); + auto final_bb = new_bb (); + add_jump_to (final_bb); + + ctx.current_bb = short_circuit_bb; + translated = ctx.place_db.get_constant (lookup_type (expr)); + push_assignment (return_place, translated); + add_jump_to (final_bb); + + ctx.current_bb = final_bb; + return return_place; + } + +protected: + void visit (HIR::LazyBooleanExpr &expr) override + { + expr.get_lhs ()->accept_vis (*this); + push_switch (translated); + add_jump_to (short_circuit_bb); + + start_new_subsequent_bb (); + expr.get_rhs ()->accept_vis (*this); + } + void visit (HIR::GroupedExpr &expr) override + { + expr.get_expr_in_parens ()->accept_vis (*this); + } + +protected: +public: + void visit (HIR::QualifiedPathInExpression &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::PathInExpression &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ClosureExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::StructExprStructFields &fields) override + { + ExprStmtBuilder (ctx).build (fields); + } + void visit (HIR::StructExprStruct &a_struct) override + { + ExprStmtBuilder (ctx).build (a_struct); + } + void visit (HIR::LiteralExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::BorrowExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::DereferenceExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ErrorPropagationExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::NegationExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ArithmeticOrLogicalExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ComparisonExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::TypeCastExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::AssignmentExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::CompoundAssignmentExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ArrayExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::ArrayIndexExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::TupleExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::TupleIndexExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::CallExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::MethodCallExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::FieldAccessExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::BlockExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::UnsafeBlockExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::LoopExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::WhileLoopExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::WhileLetLoopExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::IfExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::IfExprConseqElse &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::IfLetExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::IfLetExprConseqElse &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::MatchExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::AwaitExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + void visit (HIR::AsyncBlockExpr &expr) override + { + translated = ExprStmtBuilder (ctx).build (expr); + } + +protected: // Illegal at this position. + void visit (HIR::StructExprFieldIdentifier &field) override + { + rust_unreachable (); + } + void visit (HIR::StructExprFieldIdentifierValue &field) override + { + rust_unreachable (); + } + void visit (HIR::StructExprFieldIndexValue &field) override + { + rust_unreachable (); + } + void visit (HIR::ContinueExpr &expr) override { rust_unreachable (); } + void visit (HIR::BreakExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromToExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeToExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFullExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromToInclExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeToInclExpr &expr) override { rust_unreachable (); } + void visit (HIR::ReturnExpr &expr) override { rust_unreachable (); } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_LAZYBOOLEXPR_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h new file mode 100644 index 000000000000..0b4c83eca844 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h @@ -0,0 +1,290 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_PATTERN_H +#define RUST_BIR_BUILDER_PATTERN_H + +#include "rust-bir-builder-internal.h" + +namespace Rust { +namespace BIR { +// Compiles binding of values into newly created variables. +// Used in let, match arm, and function parameter patterns. +class PatternBindingBuilder : protected AbstractBuilder, + public HIR::HIRPatternVisitor +{ + PlaceId init; + // This is where lifetime annotations are stored. + tl::optional type; + + // Emulates recursive stack saving and restoring inside a visitor. + class SavedState + { + PatternBindingBuilder *builder; + + public: + const PlaceId init; + const tl::optional type; + + public: + explicit SavedState (PatternBindingBuilder *builder) + : builder (builder), init (builder->init), type (builder->type) + {} + + ~SavedState () + { + builder->init = init; + builder->type = type; + } + }; + +public: + PatternBindingBuilder (BuilderContext &ctx, PlaceId init, HIR::Type *type) + : AbstractBuilder (ctx), init (init), + type (type ? tl::optional (type) : tl::nullopt) + {} + + void go (HIR::Pattern &pattern) { pattern.accept_vis (*this); } + + void visit_identifier (const Analysis::NodeMapping &node, bool is_ref) + { + translated = declare_variable (node); + if (is_ref) + { + push_assignment (translated, new BorrowExpr (init)); + } + else + { + push_assignment (translated, init); + } + auto &init_place = ctx.place_db[init]; + auto &translated_place = ctx.place_db[translated]; + if (init_place.tyty->get_kind () == TyTy::REF) + { + init_place.lifetime = ctx.lookup_lifetime (type.map ([] (HIR::Type *t) { + return static_cast (t)->get_lifetime (); + })); + translated_place.lifetime = init_place.lifetime; + } + } + + void visit (HIR::IdentifierPattern &pattern) override + { + // Top-level identifiers are resolved directly to avoid useless temporary + // (for cleaner BIR). + visit_identifier (pattern.get_mappings (), pattern.get_is_ref ()); + } + void visit (HIR::ReferencePattern &pattern) override + { + SavedState saved (this); + + auto ref_type = type.map ( + [] (HIR::Type *t) { return static_cast (t); }); + + type = ref_type.map ( + [] (HIR::ReferenceType *r) { return r->get_base_type ().get (); }); + init = ctx.place_db.lookup_or_add_path (Place::DEREF, lookup_type (pattern), + saved.init); + ctx.place_db[init].lifetime + = ctx.lookup_lifetime (ref_type.map (&HIR::ReferenceType::get_lifetime)); + pattern.get_referenced_pattern ()->accept_vis (*this); + } + void visit (HIR::SlicePattern &pattern) override + { + SavedState saved (this); + + type = type.map ([] (HIR::Type *t) { + return static_cast (t)->get_element_type ().get (); + }); + // All indexes are supposed to point to the same place for borrow-checking. + init = ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (pattern), + saved.init); + for (auto &item : pattern.get_items ()) + { + item->accept_vis (*this); + } + } + void visit (HIR::AltPattern &pattern) override + { + rust_sorry_at (pattern.get_locus (), + "borrow-checking of alt patterns is not yet implemented"); + } + void visit (HIR::StructPattern &pattern) override + { + SavedState saved (this); + + auto tyty = ctx.place_db[init].tyty; + rust_assert (tyty->get_kind () == TyTy::ADT); + auto adt_ty = static_cast (tyty); + rust_assert (adt_ty->is_struct_struct ()); + auto struct_ty = adt_ty->get_variants ().at (0); + + auto struct_type = type.map ([] (HIR::Type *t) { + return static_cast (t)->get_final_segment ().get (); + }); + for (auto &field : + pattern.get_struct_pattern_elems ().get_struct_pattern_fields ()) + { + switch (field->get_item_type ()) + { + case HIR::StructPatternField::TUPLE_PAT: { + auto tuple + = static_cast (field.get ()); + init = ctx.place_db.lookup_or_add_path ( + Place::FIELD, lookup_type (*tuple->get_tuple_pattern ()), + saved.init, tuple->get_index ()); + tuple->get_tuple_pattern ()->accept_vis (*this); + break; + } + case HIR::StructPatternField::IDENT_PAT: { + auto ident_field + = static_cast (field.get ()); + TyTy::StructFieldType *field_ty = nullptr; + size_t field_index = 0; + auto ok = struct_ty->lookup_field ( + ident_field->get_identifier ().as_string (), &field_ty, + &field_index); + rust_assert (ok); + init + = ctx.place_db.lookup_or_add_path (Place::FIELD, + field_ty->get_field_type (), + saved.init, field_index); + ident_field->get_pattern ()->accept_vis (*this); + break; + } + case HIR::StructPatternField::IDENT: { + auto ident_field + = static_cast (field.get ()); + TyTy::StructFieldType *field_ty = nullptr; + size_t field_index = 0; + auto ok = struct_ty->lookup_field ( + ident_field->get_identifier ().as_string (), &field_ty, + &field_index); + rust_assert (ok); + init + = ctx.place_db.lookup_or_add_path (Place::FIELD, + field_ty->get_field_type (), + saved.init, field_index); + visit_identifier (ident_field->get_mappings (), + ident_field->get_has_ref ()); + break; + } + } + } + } + void visit_tuple_fields (std::vector> &fields, + SavedState &saved, size_t &index) + { + for (auto &item : fields) + { + type = saved.type.map ([&] (HIR::Type *t) { + return static_cast (t) + ->get_elems () + .at (index) + .get (); + }); + init + = ctx.place_db.lookup_or_add_path (Place::FIELD, lookup_type (*item), + saved.init, index); + item->accept_vis (*this); + index++; + } + } + void visit (HIR::TuplePattern &pattern) override + { + SavedState saved (this); + + size_t index = 0; + switch (pattern.get_items ()->get_item_type ()) + { + case HIR::TuplePatternItems::MULTIPLE: { + auto &items = static_cast ( + *pattern.get_items ()); + visit_tuple_fields (items.get_patterns (), saved, index); + break; + } + case HIR::TuplePatternItems::RANGED: { + auto &items = static_cast ( + *pattern.get_items ()); + + auto tyty = ctx.place_db[init].tyty; + rust_assert (tyty->get_kind () == TyTy::TUPLE); + + auto skipped = (static_cast (tyty))->num_fields () + - items.get_lower_patterns ().size () + - items.get_upper_patterns ().size (); + + visit_tuple_fields (items.get_lower_patterns (), saved, index); + index += skipped; + visit_tuple_fields (items.get_upper_patterns (), saved, index); + break; + } + } + init = saved.init; + } + void visit (HIR::TupleStructPattern &pattern) override + { + SavedState saved (this); + + size_t index = 0; + switch (pattern.get_items ()->get_item_type ()) + { + case HIR::TupleStructItems::RANGE: { + auto &items + = static_cast (*pattern.get_items ()); + + auto tyty = ctx.place_db[init].tyty; + rust_assert (tyty->get_kind () == TyTy::ADT); + auto adt_ty = static_cast (tyty); + rust_assert (adt_ty->is_tuple_struct ()); + + auto skipped = adt_ty->get_variants ().at (0)->get_fields ().size () + - items.get_lower_patterns ().size () + - items.get_upper_patterns ().size (); + + visit_tuple_fields (items.get_lower_patterns (), saved, index); + index += skipped; + visit_tuple_fields (items.get_upper_patterns (), saved, index); + break; + } + case HIR::TupleStructItems::NO_RANGE: { + auto &items = static_cast ( + *pattern.get_items ()); + visit_tuple_fields (items.get_patterns (), saved, index); + break; + } + } + } + void visit (HIR::WildcardPattern &pattern) override {} + + // Unused for binding. + void visit (HIR::LiteralPattern &pattern) override {} + void visit (HIR::PathInExpression &expression) override {} + void visit (HIR::QualifiedPathInExpression &expression) override {} + void visit (HIR::RangePattern &pattern) override {} + +private: + template tl::optional *get_type () + { + return static_cast (type); + } +}; +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_PATTERN_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h new file mode 100644 index 000000000000..fa2f5965af68 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h @@ -0,0 +1,270 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_STRUCT_H +#define RUST_BIR_BUILDER_STRUCT_H + +#include "rust-bir-builder-internal.h" +#include "rust-bir-builder-expr-stmt.h" + +namespace Rust { +namespace BIR { + +// Separated because it needs the struct type as a context. +class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor +{ + TyTy::VariantDef *struct_ty; + std::vector init_values; + +public: + StructBuilder (BuilderContext &ctx, TyTy::VariantDef *struct_ty) + : AbstractBuilder (ctx), struct_ty (struct_ty) + { + init_values.reserve (struct_ty->get_fields ().size ()); + } + + std::vector &&build (HIR::StructExprStructFields &struct_expr) + { + for (auto &field : struct_expr.get_fields ()) + field->accept_vis (*this); + return std::move (init_values); + } + + void visit (HIR::StructExprFieldIdentifier &field) override + { + resolve_variable (field, translated); + handle_named_field (field); + } + void visit (HIR::StructExprFieldIdentifierValue &field) override + { + translated = ExprStmtBuilder (ctx).build (*field.get_value ()); + handle_named_field (field); + } + void visit (HIR::StructExprFieldIndexValue &field) override + { + translated = ExprStmtBuilder (ctx).build (*field.get_value ()); + coercion_site (translated, + struct_ty->get_field_at_index (field.get_tuple_index ()) + ->get_field_type ()); + init_values.push_back (translated); + } + +private: + template void handle_named_field (T &field) + { + size_t field_index; + TyTy::StructFieldType *field_type; + bool ok = struct_ty->lookup_field (field.get_field_name ().as_string (), + &field_type, &field_index); + rust_assert (ok); + rust_assert (translated != INVALID_PLACE); + coercion_site (translated, field_type->get_field_type ()); + init_values.push_back (translated); + } + +protected: + void visit (HIR::Lifetime &lifetime) override { rust_unreachable (); } + void visit (HIR::LifetimeParam &lifetime_param) override + { + rust_unreachable (); + } + void visit (HIR::PathInExpression &path) override { rust_unreachable (); } + void visit (HIR::TypePathSegment &segment) override { rust_unreachable (); } + void visit (HIR::TypePathSegmentGeneric &segment) override + { + rust_unreachable (); + } + void visit (HIR::TypePathSegmentFunction &segment) override + { + rust_unreachable (); + } + void visit (HIR::TypePath &path) override { rust_unreachable (); } + void visit (HIR::QualifiedPathInExpression &path) override + { + rust_unreachable (); + } + void visit (HIR::QualifiedPathInType &path) override { rust_unreachable (); } + void visit (HIR::LiteralExpr &expr) override { rust_unreachable (); } + void visit (HIR::BorrowExpr &expr) override { rust_unreachable (); } + void visit (HIR::DereferenceExpr &expr) override { rust_unreachable (); } + void visit (HIR::ErrorPropagationExpr &expr) override { rust_unreachable (); } + void visit (HIR::NegationExpr &expr) override { rust_unreachable (); } + void visit (HIR::ArithmeticOrLogicalExpr &expr) override + { + rust_unreachable (); + } + void visit (HIR::ComparisonExpr &expr) override { rust_unreachable (); } + void visit (HIR::LazyBooleanExpr &expr) override { rust_unreachable (); } + void visit (HIR::TypeCastExpr &expr) override { rust_unreachable (); } + void visit (HIR::AssignmentExpr &expr) override { rust_unreachable (); } + void visit (HIR::CompoundAssignmentExpr &expr) override + { + rust_unreachable (); + } + void visit (HIR::GroupedExpr &expr) override { rust_unreachable (); } + void visit (HIR::ArrayElemsValues &elems) override { rust_unreachable (); } + void visit (HIR::ArrayElemsCopied &elems) override { rust_unreachable (); } + void visit (HIR::ArrayExpr &expr) override { rust_unreachable (); } + void visit (HIR::ArrayIndexExpr &expr) override { rust_unreachable (); } + void visit (HIR::TupleExpr &expr) override { rust_unreachable (); } + void visit (HIR::TupleIndexExpr &expr) override { rust_unreachable (); } + void visit (HIR::StructExprStruct &expr) override { rust_unreachable (); } + void visit (HIR::StructExprStructFields &expr) override + { + rust_unreachable (); + } + void visit (HIR::StructExprStructBase &expr) override { rust_unreachable (); } + void visit (HIR::CallExpr &expr) override { rust_unreachable (); } + void visit (HIR::MethodCallExpr &expr) override { rust_unreachable (); } + void visit (HIR::FieldAccessExpr &expr) override { rust_unreachable (); } + void visit (HIR::BlockExpr &expr) override { rust_unreachable (); } + void visit (HIR::ClosureExpr &expr) override { rust_unreachable (); } + void visit (HIR::ContinueExpr &expr) override { rust_unreachable (); } + void visit (HIR::BreakExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromToExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeToExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFullExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeFromToInclExpr &expr) override { rust_unreachable (); } + void visit (HIR::RangeToInclExpr &expr) override { rust_unreachable (); } + void visit (HIR::ReturnExpr &expr) override { rust_unreachable (); } + void visit (HIR::UnsafeBlockExpr &expr) override { rust_unreachable (); } + void visit (HIR::LoopExpr &expr) override { rust_unreachable (); } + void visit (HIR::WhileLoopExpr &expr) override { rust_unreachable (); } + void visit (HIR::WhileLetLoopExpr &expr) override { rust_unreachable (); } + void visit (HIR::IfExpr &expr) override { rust_unreachable (); } + void visit (HIR::IfExprConseqElse &expr) override { rust_unreachable (); } + void visit (HIR::IfLetExpr &expr) override { rust_unreachable (); } + void visit (HIR::IfLetExprConseqElse &expr) override { rust_unreachable (); } + void visit (HIR::MatchExpr &expr) override { rust_unreachable (); } + void visit (HIR::AwaitExpr &expr) override { rust_unreachable (); } + void visit (HIR::AsyncBlockExpr &expr) override { rust_unreachable (); } + void visit (HIR::TypeParam ¶m) override { rust_unreachable (); } + void visit (HIR::ConstGenericParam ¶m) override { rust_unreachable (); } + void visit (HIR::LifetimeWhereClauseItem &item) override + { + rust_unreachable (); + } + void visit (HIR::TypeBoundWhereClauseItem &item) override + { + rust_unreachable (); + } + void visit (HIR::Module &module) override { rust_unreachable (); } + void visit (HIR::ExternCrate &crate) override { rust_unreachable (); } + void visit (HIR::UseTreeGlob &use_tree) override { rust_unreachable (); } + void visit (HIR::UseTreeList &use_tree) override { rust_unreachable (); } + void visit (HIR::UseTreeRebind &use_tree) override { rust_unreachable (); } + void visit (HIR::UseDeclaration &use_decl) override { rust_unreachable (); } + void visit (HIR::Function &function) override { rust_unreachable (); } + void visit (HIR::TypeAlias &type_alias) override { rust_unreachable (); } + void visit (HIR::StructStruct &struct_item) override { rust_unreachable (); } + void visit (HIR::TupleStruct &tuple_struct) override { rust_unreachable (); } + void visit (HIR::EnumItem &item) override { rust_unreachable (); } + void visit (HIR::EnumItemTuple &item) override { rust_unreachable (); } + void visit (HIR::EnumItemStruct &item) override { rust_unreachable (); } + void visit (HIR::EnumItemDiscriminant &item) override { rust_unreachable (); } + void visit (HIR::Enum &enum_item) override { rust_unreachable (); } + void visit (HIR::Union &union_item) override { rust_unreachable (); } + void visit (HIR::ConstantItem &const_item) override { rust_unreachable (); } + void visit (HIR::StaticItem &static_item) override { rust_unreachable (); } + void visit (HIR::TraitItemFunc &item) override { rust_unreachable (); } + void visit (HIR::TraitItemConst &item) override { rust_unreachable (); } + void visit (HIR::TraitItemType &item) override { rust_unreachable (); } + void visit (HIR::Trait &trait) override { rust_unreachable (); } + void visit (HIR::ImplBlock &impl) override { rust_unreachable (); } + void visit (HIR::ExternalStaticItem &item) override { rust_unreachable (); } + void visit (HIR::ExternalFunctionItem &item) override { rust_unreachable (); } + void visit (HIR::ExternBlock &block) override { rust_unreachable (); } + void visit (HIR::LiteralPattern &pattern) override { rust_unreachable (); } + void visit (HIR::IdentifierPattern &pattern) override { rust_unreachable (); } + void visit (HIR::WildcardPattern &pattern) override { rust_unreachable (); } + void visit (HIR::RangePatternBoundLiteral &bound) override + { + rust_unreachable (); + } + void visit (HIR::RangePatternBoundPath &bound) override + { + rust_unreachable (); + } + void visit (HIR::RangePatternBoundQualPath &bound) override + { + rust_unreachable (); + } + void visit (HIR::RangePattern &pattern) override { rust_unreachable (); } + void visit (HIR::ReferencePattern &pattern) override { rust_unreachable (); } + void visit (HIR::StructPatternFieldTuplePat &field) override + { + rust_unreachable (); + } + void visit (HIR::StructPatternFieldIdentPat &field) override + { + rust_unreachable (); + } + void visit (HIR::StructPatternFieldIdent &field) override + { + rust_unreachable (); + } + void visit (HIR::StructPattern &pattern) override { rust_unreachable (); } + void visit (HIR::TupleStructItemsNoRange &tuple_items) override + { + rust_unreachable (); + } + void visit (HIR::TupleStructItemsRange &tuple_items) override + { + rust_unreachable (); + } + void visit (HIR::TupleStructPattern &pattern) override + { + rust_unreachable (); + } + void visit (HIR::TuplePatternItemsMultiple &tuple_items) override + { + rust_unreachable (); + } + void visit (HIR::TuplePatternItemsRanged &tuple_items) override + { + rust_unreachable (); + } + void visit (HIR::TuplePattern &pattern) override { rust_unreachable (); } + void visit (HIR::SlicePattern &pattern) override { rust_unreachable (); } + void visit (HIR::AltPattern &pattern) override { rust_unreachable (); } + void visit (HIR::EmptyStmt &stmt) override { rust_unreachable (); } + void visit (HIR::LetStmt &stmt) override { rust_unreachable (); } + void visit (HIR::ExprStmt &stmt) override { rust_unreachable (); } + void visit (HIR::TraitBound &bound) override { rust_unreachable (); } + void visit (HIR::ImplTraitType &type) override { rust_unreachable (); } + void visit (HIR::TraitObjectType &type) override { rust_unreachable (); } + void visit (HIR::ParenthesisedType &type) override { rust_unreachable (); } + void visit (HIR::ImplTraitTypeOneBound &type) override + { + rust_unreachable (); + } + void visit (HIR::TupleType &type) override { rust_unreachable (); } + void visit (HIR::NeverType &type) override { rust_unreachable (); } + void visit (HIR::RawPointerType &type) override { rust_unreachable (); } + void visit (HIR::ReferenceType &type) override { rust_unreachable (); } + void visit (HIR::ArrayType &type) override { rust_unreachable (); } + void visit (HIR::SliceType &type) override { rust_unreachable (); } + void visit (HIR::InferredType &type) override { rust_unreachable (); } + void visit (HIR::BareFunctionType &type) override { rust_unreachable (); } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_STRUCT_H diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h new file mode 100644 index 000000000000..322d00d280e5 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h @@ -0,0 +1,88 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_BUILDER_H +#define RUST_BIR_BUILDER_H + +#include "rust-bir-builder-internal.h" +#include "rust-hir-visitor.h" +#include "rust-bir-builder-pattern.h" +#include "rust-bir-builder-struct.h" +#include "rust-bir-builder-expr-stmt.h" + +namespace Rust { +namespace BIR { + +/** Top-level builder, which compiles a HIR function into a BIR function. */ +class Builder : public AbstractBuilder +{ +public: + explicit Builder (BuilderContext &ctx) : AbstractBuilder (ctx) {} + + Function build (HIR::Function &function) + { + PlaceId return_place + = ctx.place_db.add_temporary (lookup_type (*function.get_definition ())); + rust_assert (return_place == RETURN_VALUE_PLACE); + + for (auto ¶m : function.get_function_params ()) + { + handle_param (param); + } + + handle_body (*function.get_definition ()); + + return Function{std::move (ctx.place_db), std::move (ctx.arguments), + std::move (ctx.basic_blocks)}; + }; + +private: + void handle_param (HIR::FunctionParam ¶m) + { + auto &pattern = param.get_param_name (); + if (pattern->get_pattern_type () == HIR::Pattern::IDENTIFIER + && !static_cast (*pattern).get_is_ref ()) + { + // Avoid useless temporary variable for parameter. + translated = declare_variable (pattern->get_mappings ()); + ctx.arguments.push_back (translated); + } + else + { + translated = ctx.place_db.add_temporary (lookup_type (*pattern)); + ctx.arguments.push_back (translated); + PatternBindingBuilder (ctx, translated, param.get_type ().get ()) + .go (*param.get_param_name ()); + } + } + + void handle_body (HIR::BlockExpr &body) + { + translated = ExprStmtBuilder (ctx).build (body); + if (body.has_expr ()) + { + push_assignment (RETURN_VALUE_PLACE, translated); + ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN); + } + } +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_BUILDER_H diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index 44d33c57838a..4b9712538df3 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -18,8 +18,7 @@ #include "rust-borrow-checker.h" #include "rust-function-collector.h" -#include "rust-bir.h" -#include "rust-bir-visitor.h" +#include "rust-bir-builder.h" namespace Rust { namespace HIR { @@ -30,8 +29,11 @@ BorrowChecker::go (HIR::Crate &crate) FunctionCollector collector; collector.go (crate); - for (auto func ATTRIBUTE_UNUSED : collector.get_functions ()) + for (auto func : collector.get_functions ()) { + BIR::BuilderContext ctx; + BIR::Builder builder (ctx); + builder.build (*func); } for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ()) diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index 96d5f4729165..c18a3d01bd94 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -46,12 +46,12 @@ #include "rust-expand-visitor.h" #include "rust-unicode.h" #include "rust-attribute-values.h" +#include "rust-borrow-checker.h" #include "input.h" #include "selftest.h" #include "tm.h" #include "rust-target.h" -#include "rust-borrow-checker.h" extern bool saw_errors (void); From 13e67dd1dc446b8ef77820511b585893a619e4d9 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 22:36:38 +0200 Subject: [PATCH 06/16] borrowck: BIR dump gcc/rust/ChangeLog: * Make-lang.in: Build BIR dump. * checks/errors/borrowck/rust-borrow-checker.cc (mkdir_wrapped): Cross-platform mkdir. (dump_function_bir): Save dump to file. (BorrowChecker::go): Run dump during borrowck. * checks/errors/borrowck/rust-bir-dump.cc: New file. * checks/errors/borrowck/rust-bir-dump.h: New file. Signed-off-by: Jakub Dupak --- gcc/rust/Make-lang.in | 1 + .../checks/errors/borrowck/rust-bir-dump.cc | 239 ++++++++++++++++++ .../checks/errors/borrowck/rust-bir-dump.h | 62 +++++ .../errors/borrowck/rust-borrow-checker.cc | 56 +++- 4 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-dump.cc create mode 100644 gcc/rust/checks/errors/borrowck/rust-bir-dump.h diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 1e462ef6a1bf..facdd43fd518 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -150,6 +150,7 @@ GRS_OBJS = \ rust/rust-hir-type-check-implitem.o \ rust/rust-borrow-checker.o \ rust/rust-bir-builder-expr-stmt.o \ + rust/rust-bir-dump.o \ rust/rust-hir-dot-operator.o \ rust/rust-hir-path-probe.o \ rust/rust-type-util.o \ diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc new file mode 100644 index 000000000000..cebed2485a31 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -0,0 +1,239 @@ +#include "rust-bir-dump.h" + +namespace Rust { +namespace BIR { + +constexpr auto indentation = " "; + +uint32_t +get_place_name (PlaceId place_id) +{ + rust_assert (place_id >= FIRST_VARIABLE_PLACE); + return place_id - FIRST_VARIABLE_PLACE; +} + +uint32_t +get_lifetime_name (Lifetime lifetime_id) +{ + rust_assert (lifetime_id.id >= FIRST_NORMAL_LIFETIME_ID); + // Start from 1 as rustc does. + return lifetime_id.id - FIRST_NORMAL_LIFETIME_ID + 1; +} + +std::string +get_tyty_name (TyTy::BaseType *tyty) +{ + if (tyty) + return tyty->get_name (); + return "unknown"; +} + +void +Dump::go () +{ + stream << "fn " << name << "("; + for (PlaceId arg : func.arguments) + { + stream << "_" << get_place_name (arg) << ": " + << get_tyty_name (place_db[arg].tyty) << ", "; + } + stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty) + << " {\n"; + + for (PlaceId id = FIRST_VARIABLE_PLACE; id < place_db.size (); ++id) + { + const Place &place = place_db[id]; + if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY) + { + if (std::find (func.arguments.begin (), func.arguments.end (), id) + != func.arguments.end ()) + continue; + stream << indentation << "let _" << get_place_name (id) << ": " + << get_tyty_name (place_db[id].tyty) << ";\n"; + } + } + + for (BasicBlockId id = 0; id < func.basic_blocks.size (); ++id) + { + if (func.basic_blocks[id].statements.empty () + && func.basic_blocks[id].successors.empty ()) + continue; + + BasicBlock &bb = func.basic_blocks[id]; + stream << "\n"; + stream << indentation << "bb" << id << ": {\n"; + for (auto &stmt : bb.statements) + { + stream << indentation << indentation; + visit (stmt); + stream << ";\n"; + } + stream << indentation << "} -> ["; + for (auto succ : bb.successors) + stream << "bb" << succ << ", "; + stream << "]\n"; + } + + stream << "}\n\n"; +} +void +Dump::visit (Node &node) +{ + node_place = node.get_place (); + switch (node.get_kind ()) + { + case Node::Kind::ASSIGNMENT: { + stream << "_" << get_place_name (node.get_place ()) << " = "; + node.get_expr ().accept_vis (*this); + break; + } + case Node::Kind::SWITCH: + stream << "switch "; + visit_move_place (node.get_place ()); + break; + case Node::Kind::RETURN: + stream << "return"; + break; + case Node::Kind::GOTO: + stream << "goto"; + break; + case Node::Kind::STORAGE_DEAD: + stream << "StorageDead("; + visit_move_place (node.get_place ()); + stream << ")"; + break; + case Node::Kind::STORAGE_LIVE: + stream << "StorageLive("; + visit_move_place (node.get_place ()); + stream << ")"; + break; + } + node_place = INVALID_PLACE; +} + +void +Dump::visit_place (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + switch (place.kind) + { + case Place::TEMPORARY: + case Place::VARIABLE: + stream << "_" << get_place_name (place_id); + break; + case Place::DEREF: + stream << "("; + stream << "*"; + visit_place (place.path.parent); + stream << ")"; + break; + case Place::FIELD: + stream << "("; + visit_place (place.path.parent); + stream << "."; + stream << place.variable_or_field_index; + stream << ": " << get_tyty_name (place.tyty) << ")"; + break; + case Place::INDEX: + stream << "("; + visit_place (place.path.parent); + stream << "[]"; + stream << ": " << get_tyty_name (place.tyty) << ")"; + break; + case Place::CONSTANT: + stream << "const " << get_tyty_name (place.tyty); + break; + case Place::INVALID: + stream << "_INVALID"; + } +} + +void +Dump::visit_move_place (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + if (!place.is_copy) + stream << "move "; + visit_place (place_id); +} + +void +Dump::visit (BorrowExpr &expr) +{ + stream << "&"; + visit_lifetime (node_place); + visit_place (expr.get_place ()); +} + +void +Dump::visit_lifetime (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + if (place.lifetime.has_lifetime ()) + { + if (place.lifetime.id == STATIC_LIFETIME_ID) + stream << "'static "; + else + stream << "'#" << get_lifetime_name (place.lifetime) << " "; + } +} + +void +Dump::visit (InitializerExpr &expr) +{ + stream << "{"; + for (auto &place : expr.get_values ()) + { + visit_move_place (place); + stream << ", "; + } + stream << "}"; +} + +void +Dump::visit (CallExpr &expr) +{ + stream << "Call("; + if (auto fn_type + = place_db[expr.get_callable ()].tyty->try_as ()) + { + stream << fn_type->get_identifier (); + } + else + { + visit_move_place (expr.get_callable ()); + } + stream << ")("; + for (auto &place : expr.get_arguments ()) + { + visit_move_place (place); + stream << ", "; + } + stream << ")"; +} + +void +Dump::visit (Operator<1> &expr) +{ + stream << "Operator("; + visit_move_place (expr.get_operand<0> ()); + stream << ")"; +} + +void +Dump::visit (Operator<2> &expr) +{ + stream << "Operator("; + visit_move_place (expr.get_operand<0> ()); + stream << ", "; + visit_move_place (expr.get_operand<1> ()); + stream << ")"; +} +void +Dump::visit (Assignment &expr) +{ + visit_move_place (expr.get_rhs ()); +} + +} // namespace BIR +} // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h new file mode 100644 index 000000000000..7c24749fed55 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -0,0 +1,62 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_BIR_DUMP_H +#define RUST_BIR_DUMP_H + +#include +#include +#include "rust-bir-place.h" +#include "rust-bir-visitor.h" +#include "rust-bir.h" + +namespace Rust { +namespace BIR { + +class Dump : public Visitor +{ + std::ostream &stream; + const PlaceDB &place_db; + Function &func; + const std::string &name; + + PlaceId node_place = INVALID_PLACE; + +public: + Dump (std::ostream &os, Function &func, const std::string &name) + : stream (os), place_db (func.place_db), func (func), name (name) + {} + void go (); + +protected: + void visit (Node &node) override; + void visit_place (PlaceId place_id); + void visit_move_place (PlaceId place_id); + void visit (BorrowExpr &expr) override; + void visit_lifetime (PlaceId place_id); + void visit (InitializerExpr &expr) override; + void visit (CallExpr &expr) override; + void visit (Operator<1> &expr) override; + void visit (Operator<2> &expr) override; + void visit (Assignment &expr) override; +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_DUMP_H diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index 4b9712538df3..0c952ad32fa5 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -19,13 +19,56 @@ #include "rust-borrow-checker.h" #include "rust-function-collector.h" #include "rust-bir-builder.h" +#include "rust-bir-dump.h" namespace Rust { namespace HIR { +void +mkdir_wrapped (const std::string &dirname) +{ + int ret; +#ifdef _WIN32 + ret = _mkdir (dirname.c_str ()); +#elif unix + ret = mkdir (dirname.c_str (), 0775); +#elif __APPLE__ + ret = mkdir (dirname.c_str (), 0775); +#endif + (void) ret; +} + +void +dump_function_bir (const std::string &filename, BIR::Function &func, + const std::string &name) +{ + std::ofstream file; + file.open (filename); + if (file.fail ()) + { + rust_error_at (UNKNOWN_LOCATION, "Failed to open file %s", + filename.c_str ()); + return; + } + BIR::Dump (file, func, name).go (); + file.close (); +} + void BorrowChecker::go (HIR::Crate &crate) { + std::string crate_name; + + if (enable_dump_bir) + { + mkdir_wrapped ("bir_dump"); + auto mappings = Analysis::Mappings::get (); + bool ok + = mappings->get_crate_name (crate.get_mappings ().get_crate_num (), + crate_name); + rust_assert (ok); + } + FunctionCollector collector; collector.go (crate); @@ -33,11 +76,22 @@ BorrowChecker::go (HIR::Crate &crate) { BIR::BuilderContext ctx; BIR::Builder builder (ctx); - builder.build (*func); + auto bir = builder.build (*func); + + if (enable_dump_bir) + { + std::string filename = "bir_dump/" + crate_name + "." + + func->get_function_name ().as_string () + + ".bir.dump"; + dump_function_bir (filename, bir, + func->get_function_name ().as_string ()); + } } for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ()) { + rust_sorry_at (closure->get_locus (), + "Closure borrow checking is not implemented yet."); } } From 42000a1cca8f724d7323e2d05191189a17f6344e Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 22:38:30 +0200 Subject: [PATCH 07/16] borrowck: Dump: proper comma separation gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-dump.cc (Dump::go): Use new print. (print_comma_separated): Comma separation print. (Dump::visit): Use new print. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-bir-dump.cc | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index cebed2485a31..4571b2fe8577 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -28,15 +28,31 @@ get_tyty_name (TyTy::BaseType *tyty) return "unknown"; } +template void -Dump::go () +print_comma_separated (std::ostream &stream, const std::vector &collection, + FN printer) { - stream << "fn " << name << "("; - for (PlaceId arg : func.arguments) + if (collection.empty ()) + return; + printer (collection[0]); + for (auto it = collection.begin () + 1; it != collection.end (); ++it) { - stream << "_" << get_place_name (arg) << ": " - << get_tyty_name (place_db[arg].tyty) << ", "; + stream << ", "; + printer (*it); } +} + +static constexpr bool FOLD_CFG = true; + +void +Dump::go () +{ + stream << "fn " << name << "("; + print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) { + stream << "_" << get_place_name (place_id) << ": " + << get_tyty_name (place_db[place_id].tyty); + }); stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty) << " {\n"; @@ -182,11 +198,9 @@ void Dump::visit (InitializerExpr &expr) { stream << "{"; - for (auto &place : expr.get_values ()) - { - visit_move_place (place); - stream << ", "; - } + print_comma_separated (stream, expr.get_values (), [this] (PlaceId place_id) { + visit_move_place (place_id); + }); stream << "}"; } From c304940f73b7d8ad36e50a0cef5e0f8596e59921 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 22:50:02 +0200 Subject: [PATCH 08/16] borrowck: Dump: simplify cfg Add option to simplify cfg for better readability. Off by default. gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-dump.cc (simplify_cfg): Simplify cfg logic. (Dump::go): Run simplify cfg. * checks/errors/borrowck/rust-bir-dump.h: Option to simplify cfg. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-bir-dump.cc | 40 +++++++++++++++++-- .../checks/errors/borrowck/rust-bir-dump.h | 7 +++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index 4571b2fe8577..709d36a7cd33 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -1,3 +1,4 @@ +#include #include "rust-bir-dump.h" namespace Rust { @@ -43,11 +44,39 @@ print_comma_separated (std::ostream &stream, const std::vector &collection, } } -static constexpr bool FOLD_CFG = true; +void +simplify_cfg (Function &func, std::vector &bb_fold_map) +{ + // The BIR builder can generate many useless basic blocks, which contain only + // a goto. + // For actual borrow-checking, the folding has little value. + + bool stabilized = false; + while (!stabilized) + { + stabilized = true; + // BB0 cannot be folded as it is an entry block. + for (size_t i = 1; i < func.basic_blocks.size (); ++i) + { + const BasicBlock &bb = func.basic_blocks[bb_fold_map[i]]; + if (bb.statements.empty () && bb.is_goto_terminated ()) + { + bb_fold_map[i] = bb.successors.at (0); + stabilized = false; + } + } + } +} void -Dump::go () +Dump::go (bool enable_simplify_cfg) { + // To avoid mutation of the BIR, we use indirection through bb_fold_map. + std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0); + + if (enable_simplify_cfg) + simplify_cfg (func, bb_fold_map); + stream << "fn " << name << "("; print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) { stream << "_" << get_place_name (place_id) << ": " @@ -71,13 +100,16 @@ Dump::go () for (BasicBlockId id = 0; id < func.basic_blocks.size (); ++id) { + if (bb_fold_map[id] != id) + continue; // This BB was folded. + if (func.basic_blocks[id].statements.empty () && func.basic_blocks[id].successors.empty ()) continue; BasicBlock &bb = func.basic_blocks[id]; stream << "\n"; - stream << indentation << "bb" << id << ": {\n"; + stream << indentation << "bb" << bb_fold_map[id] << ": {\n"; for (auto &stmt : bb.statements) { stream << indentation << indentation; @@ -86,7 +118,7 @@ Dump::go () } stream << indentation << "} -> ["; for (auto succ : bb.successors) - stream << "bb" << succ << ", "; + stream << "bb" << bb_fold_map[succ] << ", "; stream << "]\n"; } diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h index 7c24749fed55..1f8de045c22c 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -35,13 +35,16 @@ class Dump : public Visitor Function &func; const std::string &name; + std::vector bb_fold_map; + PlaceId node_place = INVALID_PLACE; public: Dump (std::ostream &os, Function &func, const std::string &name) - : stream (os), place_db (func.place_db), func (func), name (name) + : stream (os), place_db (func.place_db), func (func), name (name), + bb_fold_map (func.basic_blocks.size ()) {} - void go (); + void go (bool enable_simplify_cfg = false); protected: void visit (Node &node) override; From d70becd32e3a1a32a71bf79cd386daa65dc3f74d Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 23:27:48 +0200 Subject: [PATCH 09/16] borrowck: Dump improve jumps gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-dump.cc (Dump::go): Improve jumps dump. (Dump::visit): Improve jumps dump. * checks/errors/borrowck/rust-bir-dump.h (class Dump): Improve jumps dump. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-bir-dump.cc | 77 +++++++++++++------ .../checks/errors/borrowck/rust-bir-dump.h | 4 +- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index 709d36a7cd33..ba530983dade 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -6,13 +6,6 @@ namespace BIR { constexpr auto indentation = " "; -uint32_t -get_place_name (PlaceId place_id) -{ - rust_assert (place_id >= FIRST_VARIABLE_PLACE); - return place_id - FIRST_VARIABLE_PLACE; -} - uint32_t get_lifetime_name (Lifetime lifetime_id) { @@ -44,6 +37,28 @@ print_comma_separated (std::ostream &stream, const std::vector &collection, } } +void +renumber_places (const Function &func, std::vector &place_map) +{ + // Renumbering places to avoid gaps in the place id space. + // This is needed to match MIR shape. + size_t next_out_id = 0; + + for (size_t in_id = FIRST_VARIABLE_PLACE; in_id < func.place_db.size (); + ++in_id) + { + const Place &place = func.place_db[in_id]; + if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY) + { + place_map[in_id] = next_out_id++; + } + else + { + place_map[in_id] = INVALID_PLACE; + } + } +} + void simplify_cfg (Function &func, std::vector &bb_fold_map) { @@ -74,12 +89,16 @@ Dump::go (bool enable_simplify_cfg) // To avoid mutation of the BIR, we use indirection through bb_fold_map. std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0); + std::iota (place_map.begin (), place_map.end (), 0); + if (enable_simplify_cfg) simplify_cfg (func, bb_fold_map); + renumber_places (func, place_map); + stream << "fn " << name << "("; print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) { - stream << "_" << get_place_name (place_id) << ": " + stream << "_" << place_map[place_id] << ": " << get_tyty_name (place_db[place_id].tyty); }); stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty) @@ -93,33 +112,30 @@ Dump::go (bool enable_simplify_cfg) if (std::find (func.arguments.begin (), func.arguments.end (), id) != func.arguments.end ()) continue; - stream << indentation << "let _" << get_place_name (id) << ": " + stream << indentation << "let _" << place_map[id] << ": " << get_tyty_name (place_db[id].tyty) << ";\n"; } } - for (BasicBlockId id = 0; id < func.basic_blocks.size (); ++id) + for (node_bb = 0; node_bb < func.basic_blocks.size (); ++node_bb) { - if (bb_fold_map[id] != id) + if (bb_fold_map[node_bb] != node_bb) continue; // This BB was folded. - if (func.basic_blocks[id].statements.empty () - && func.basic_blocks[id].successors.empty ()) + if (func.basic_blocks[node_bb].statements.empty () + && func.basic_blocks[node_bb].successors.empty ()) continue; - BasicBlock &bb = func.basic_blocks[id]; + BasicBlock &bb = func.basic_blocks[node_bb]; stream << "\n"; - stream << indentation << "bb" << bb_fold_map[id] << ": {\n"; + stream << indentation << "bb" << bb_fold_map[node_bb] << ": {\n"; for (auto &stmt : bb.statements) { stream << indentation << indentation; visit (stmt); stream << ";\n"; } - stream << indentation << "} -> ["; - for (auto succ : bb.successors) - stream << "bb" << bb_fold_map[succ] << ", "; - stream << "]\n"; + stream << indentation << "}\n"; } stream << "}\n\n"; @@ -131,19 +147,26 @@ Dump::visit (Node &node) switch (node.get_kind ()) { case Node::Kind::ASSIGNMENT: { - stream << "_" << get_place_name (node.get_place ()) << " = "; + stream << "_" << place_map[node.get_place ()] << " = "; node.get_expr ().accept_vis (*this); break; } case Node::Kind::SWITCH: - stream << "switch "; + stream << "switchInt("; visit_move_place (node.get_place ()); + stream << ") -> ["; + print_comma_separated (stream, func.basic_blocks[node_bb].successors, + [this] (BasicBlockId succ) { + stream << "bb" << bb_fold_map[succ]; + }); + stream << "]"; break; case Node::Kind::RETURN: stream << "return"; break; case Node::Kind::GOTO: - stream << "goto"; + stream << "goto -> bb" + << bb_fold_map[func.basic_blocks[node_bb].successors.at (0)]; break; case Node::Kind::STORAGE_DEAD: stream << "StorageDead("; @@ -167,7 +190,7 @@ Dump::visit_place (PlaceId place_id) { case Place::TEMPORARY: case Place::VARIABLE: - stream << "_" << get_place_name (place_id); + stream << "_" << place_map[place_id]; break; case Place::DEREF: stream << "("; @@ -255,7 +278,13 @@ Dump::visit (CallExpr &expr) visit_move_place (place); stream << ", "; } - stream << ")"; + stream << ") -> ["; + print_comma_separated (stream, + func.basic_blocks[bb_fold_map[node_bb]].successors, + [this] (const BasicBlockId &dst) { + stream << "bb" << bb_fold_map[dst]; + }); + stream << "]"; } void diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h index 1f8de045c22c..edf7d1ea1ec3 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -36,13 +36,15 @@ class Dump : public Visitor const std::string &name; std::vector bb_fold_map; + std::vector place_map; PlaceId node_place = INVALID_PLACE; + BasicBlockId node_bb = INVALID_BB; public: Dump (std::ostream &os, Function &func, const std::string &name) : stream (os), place_db (func.place_db), func (func), name (name), - bb_fold_map (func.basic_blocks.size ()) + bb_fold_map (func.basic_blocks.size ()), place_map (func.place_db.size ()) {} void go (bool enable_simplify_cfg = false); From f13ab059f93131c114fcd1d656e86e18ab45bd9b Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 22:54:37 +0200 Subject: [PATCH 10/16] borrowck: BIR: handle break gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit): Push ctx. (ExprStmtBuilder::setup_loop): Common loop infractructure setup. * checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Loop ctx. * checks/errors/borrowck/rust-bir-builder-internal.h (struct BuilderContext): Loop ctx. Signed-off-by: Jakub Dupak --- .../borrowck/rust-bir-builder-expr-stmt.cc | 168 +++++++++++++----- .../borrowck/rust-bir-builder-expr-stmt.h | 2 + .../borrowck/rust-bir-builder-internal.h | 66 ++++--- 3 files changed, 168 insertions(+), 68 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index 1487c853b49e..67b98e2254f9 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -258,11 +258,45 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) void ExprStmtBuilder::visit (HIR::BlockExpr &block) { + BasicBlockId end_bb; + + if (block.has_label ()) + { + end_bb = new_bb (); + NodeId label + = block.get_label ().get_lifetime ().get_mappings ().get_nodeid (); + PlaceId label_var = ctx.place_db.add_temporary (lookup_type (block)); + ctx.loop_and_label_stack.push_back ({false, label, label_var, end_bb, 0}); + } + + bool unreachable = false; for (auto &stmt : block.get_statements ()) { + if (unreachable) + break; stmt->accept_vis (*this); + if (ctx.get_current_bb ().is_terminated ()) + unreachable = true; + } + + if (block.has_label ()) + { + auto label_info = ctx.loop_and_label_stack.back (); + if (block.has_expr () && !unreachable) + { + push_assignment (label_info.label_var, + visit_expr (*block.get_final_expr ())); + } + if (!ctx.get_current_bb ().is_terminated ()) + { + add_jump_to (end_bb); + } + ctx.current_bb = end_bb; + ctx.loop_and_label_stack.pop_back (); + + return_place (label_info.label_var); } - if (block.has_expr ()) + else if (block.has_expr () && !unreachable) { return_place (visit_expr (*block.get_final_expr ())); } @@ -290,25 +324,36 @@ ExprStmtBuilder::visit (HIR::ContinueExpr &cont) void ExprStmtBuilder::visit (HIR::BreakExpr &brk) { - // BuilderContext::LabelledBlockCtx block_ctx{}; - // NodeId label = UNKNOWN_NODEID; - // if (brk.has_label ()) - // { - // if (!resolve_label (brk.get_label (), label)) - // return; - // } - // if (!find_block_ctx (label, block_ctx)) - // { - // rust_error_at (brk.get_locus (), "unresolved labelled block"); - // } - // - // if (brk.has_break_expr ()) - // { - // brk.get_expr ()->accept_vis (*this); - // push_assignment (block_ctx.label_var, new Operator<1> ({translated})); - // } - // - // add_jump_to (block_ctx.break_bb); + BuilderContext::LoopAndLabelInfo info; + if (brk.has_label ()) + { + NodeId label = resolve_label (brk.get_label ()); + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [label] (const BuilderContext::LoopAndLabelInfo &info) { + return info.label == label; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + else + { + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [] (const BuilderContext::LoopAndLabelInfo &info) { + return info.is_loop; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + if (brk.has_break_expr ()) + { + push_assignment (info.label_var, visit_expr (*brk.get_expr ())); + } + add_jump_to (info.break_bb); + // No code allowed after break. No BB starts - would be empty. } void @@ -370,60 +415,87 @@ ExprStmtBuilder::visit (HIR::UnsafeBlockExpr &expr) rust_sorry_at (expr.get_locus (), "unsafe blocks are not supported"); } +BuilderContext::LoopAndLabelInfo & +ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) +{ + NodeId label = (expr.has_loop_label ()) + ? resolve_label (expr.get_loop_label ()) + : UNKNOWN_NODEID; + PlaceId label_var = ctx.place_db.add_temporary (lookup_type (expr)); + + BasicBlockId continue_bb = new_bb (); + BasicBlockId break_bb = new_bb (); + ctx.loop_and_label_stack.push_back ( + {true, label, label_var, break_bb, continue_bb}); + return ctx.loop_and_label_stack.back (); +} + void ExprStmtBuilder::visit (HIR::LoopExpr &expr) { - // PlaceId label_var = ctx.place_db.add_temporary (nullptr); - // NodeId label; - // if (!resolve_label (expr.get_loop_label (), label)) - // return; - // ctx.label_place_map.emplace (label, label_var); - // - // expr.get_loop_block ()->accept_vis (*this); - // - // translated = label_var; + auto loop = setup_loop (expr); + + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.continue_bb; + (void) visit_expr (*expr.get_loop_block ()); + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.break_bb; } + void ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) { - // // TODO: Desugar in AST->HIR ??? - // PlaceId label_var = ctx.place_db.add_temporary (nullptr); - // NodeId label; - // if (!resolve_label (expr.get_loop_label (), label)) - // return; - // ctx.label_place_map.emplace (label, label_var); - // - // expr.get_predicate_expr ()->accept_vis (*this); - // - // expr.get_loop_block ()->accept_vis (*this); - // - // translated = label_var; + auto loop = setup_loop (expr); + + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.continue_bb; + auto cond_val = visit_expr (*expr.get_predicate_expr ()); + auto body_bb = new_bb (); + push_switch (cond_val, {body_bb, loop.break_bb}); + + ctx.current_bb = body_bb; + (void) visit_expr (*expr.get_loop_block ()); + add_jump_to (loop.continue_bb); + + ctx.current_bb = loop.break_bb; } + void ExprStmtBuilder::visit (HIR::WhileLetLoopExpr &expr) { // TODO: Desugar in AST->HIR + rust_sorry_at (expr.get_locus (), "while let loops are not yet supported"); } + void ExprStmtBuilder::visit (HIR::IfExpr &expr) { // If without else cannot return a non-unit value (see [E0317]). + if (expr.get_if_block ()->statements.empty ()) + return; + push_switch (visit_expr (*expr.get_if_condition ())); BasicBlockId if_block = ctx.current_bb; ctx.current_bb = new_bb (); + BasicBlockId then_start_block = ctx.current_bb; (void) visit_expr (*expr.get_if_block ()); - BasicBlockId then_block = ctx.current_bb; + BasicBlockId then_end_block = ctx.current_bb; ctx.current_bb = new_bb (); BasicBlockId final_block = ctx.current_bb; return_unit (expr); // Jumps are added at the end to match rustc MIR order for easier comparison. - add_jump (if_block, then_block); + add_jump (if_block, then_start_block); add_jump (if_block, final_block); - add_jump (then_block, final_block); + + if (!ctx.basic_blocks[then_end_block].is_terminated ()) + add_jump (then_end_block, final_block); } void @@ -451,8 +523,11 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) // Jumps are added at the end to match rustc MIR order for easier comparison. add_jump (if_block, then_block); add_jump (if_block, else_block); - add_jump (then_block, final_block); - add_jump (else_block, final_block); + + if (!ctx.basic_blocks[then_block].is_terminated ()) + add_jump (then_block, final_block); + if (!ctx.basic_blocks[else_block].is_terminated ()) + add_jump (else_block, final_block); } void ExprStmtBuilder::visit (HIR::IfLetExpr &expr) @@ -573,6 +648,5 @@ ExprStmtBuilder::visit (HIR::ExprStmt &stmt) { (void) visit_expr (*stmt.get_expr ()); } - } // namespace BIR } // namespace Rust \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h index f46cba5f9684..e5707c31f8d3 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h @@ -46,6 +46,8 @@ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor return result; } + BuilderContext::LoopAndLabelInfo &setup_loop (HIR::BaseLoopExpr &expr); + protected: // Expr // TODO: test when compiles void visit (HIR::ClosureExpr &expr) override; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h index 48116d8c351c..ef48cba7b80b 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -55,12 +55,21 @@ class LifetimeResolver struct BuilderContext { - struct LabelledBlockCtx + struct LoopAndLabelInfo { - NodeId label; // UNKNOWN_NODEID if no label - PlaceId label_var; + bool is_loop; // Loop or labelled block + NodeId label; // UNKNOWN_NODEID if no label (loop) + PlaceId label_var; // For break with value. BasicBlockId break_bb; BasicBlockId continue_bb; // Only valid for loops + + LoopAndLabelInfo (bool is_loop = false, NodeId label = UNKNOWN_NODEID, + PlaceId label_var = INVALID_PLACE, + BasicBlockId break_bb = INVALID_BB, + BasicBlockId continue_bb = INVALID_BB) + : is_loop (is_loop), label (label), label_var (label_var), + break_bb (break_bb), continue_bb (continue_bb) + {} }; // Context @@ -83,7 +92,7 @@ struct BuilderContext */ std::unordered_map label_place_map; - std::vector loop_stack; + std::vector loop_and_label_stack; public: BuilderContext () @@ -115,6 +124,18 @@ struct BuilderContext } rust_unreachable (); }; + + const LoopAndLabelInfo &lookup_label (NodeId label) + { + auto label_match = [label] (const LoopAndLabelInfo &info) { + return info.label != UNKNOWN_NODEID && info.label == label; + }; + + auto found = std::find_if (loop_and_label_stack.rbegin (), + loop_and_label_stack.rend (), label_match); + rust_assert (found != loop_and_label_stack.rend ()); + return *found; + } }; // Common infrastructure for building BIR from HIR. @@ -195,10 +216,13 @@ class AbstractBuilder push_assignment (tmp, rhs); } - void push_switch (PlaceId switch_val) + void push_switch (PlaceId switch_val, + std::initializer_list destinations = {}) { ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, switch_val); + ctx.get_current_bb ().successors.insert ( + ctx.get_current_bb ().successors.end (), destinations); } void push_storage_dead (PlaceId place) @@ -235,15 +259,14 @@ class AbstractBuilder void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); } protected: - template bool resolve_label (T &label, NodeId &resolved_label) + template NodeId resolve_label (T &expr) { - if (!ctx.resolver.lookup_resolved_label ( - label.get_mappings ().get_nodeid (), &resolved_label)) - { - rust_error_at (label.get_locus (), "unresolved label"); - return false; - } - return true; + NodeId resolved_label; + bool ok + = ctx.resolver.lookup_resolved_label (expr.get_mappings ().get_nodeid (), + &resolved_label); + rust_assert (ok); + return resolved_label; } template @@ -261,21 +284,22 @@ class AbstractBuilder return true; } - bool find_block_ctx (NodeId label, BuilderContext::LabelledBlockCtx &block) + bool find_block_ctx (NodeId label, BuilderContext::LoopAndLabelInfo &block) { - if (ctx.loop_stack.empty ()) + if (ctx.loop_and_label_stack.empty ()) return false; if (label == UNKNOWN_NODEID) { - block = ctx.loop_stack.back (); + block = ctx.loop_and_label_stack.back (); return true; } auto found - = std::find_if (ctx.loop_stack.rbegin (), ctx.loop_stack.rend (), - [&label] (const BuilderContext::LabelledBlockCtx &block) { + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [&label] (const BuilderContext::LoopAndLabelInfo &block) { return block.label == label; }); - if (found == ctx.loop_stack.rend ()) + if (found == ctx.loop_and_label_stack.rend ()) return false; block = *found; return true; @@ -318,8 +342,8 @@ class AbstractBuilder } } - /** Dereferences the `translated` place until it is at most one reference and - * return the base type. */ + /** Dereferences the `translated` place until it is at most one reference + * and return the base type. */ TyTy::BaseType *autoderef (PlaceId &place) { auto ty = ctx.place_db[place].tyty; From 917b6bde85cb26c63d9ab4067ee6c3b8929f75f6 Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Thu, 19 Oct 2023 11:04:29 +0200 Subject: [PATCH 11/16] borrowck: Dump: handle infinite loops gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-dump.cc (simplify_cfg): Detech infinite loops. Signed-off-by: Jakub Dupak --- .../checks/errors/borrowck/rust-bir-dump.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index ba530983dade..66870ddeb565 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -1,5 +1,6 @@ #include #include "rust-bir-dump.h" +#include "rust-diagnostics.h" namespace Rust { namespace BIR { @@ -76,7 +77,21 @@ simplify_cfg (Function &func, std::vector &bb_fold_map) const BasicBlock &bb = func.basic_blocks[bb_fold_map[i]]; if (bb.statements.empty () && bb.is_goto_terminated ()) { - bb_fold_map[i] = bb.successors.at (0); + auto dst = bb.successors.at (0); + if (bb_fold_map[dst] != dst) + { + rust_error_at ( + UNKNOWN_LOCATION, + "BIR DUMP: Cannot fold CFG, because it contains an " + "infinite loop with no executable statements."); + rust_inform (UNKNOWN_LOCATION, + "Continuing with an unfolded CFG."); + // Reverting the fold map to the original state. + std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0); + stabilized = true; + break; + } + bb_fold_map[i] = dst; stabilized = false; } } From fa9b72f16819867890a761550b4f972e6176c18c Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 23:01:06 +0200 Subject: [PATCH 12/16] borrowck: BIR continue gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit): Continue. (ExprStmtBuilder::setup_loop): Continue. Signed-off-by: Jakub Dupak --- .../borrowck/rust-bir-builder-expr-stmt.cc | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index 67b98e2254f9..9a850facff96 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -305,20 +305,32 @@ ExprStmtBuilder::visit (HIR::BlockExpr &block) void ExprStmtBuilder::visit (HIR::ContinueExpr &cont) { - // BuilderContext::LabelledBlockCtx loop_ctx; - // NodeId label = UNKNOWN_NODEID; - // if (cont.has_label ()) - // { - // if (!resolve_label (cont.get_label (), label)) - // return; - // } - // - // if (!find_block_ctx (label, loop_ctx)) - // { - // rust_error_at (cont.get_locus (), "unresolved loop label"); - // } - // - // add_jump_to (loop_ctx.continue_bb); + BuilderContext::LoopAndLabelInfo info; + if (cont.has_label ()) + { + NodeId label = resolve_label (cont.get_label ()); + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [label] (const BuilderContext::LoopAndLabelInfo &info) { + return info.label == label; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + else + { + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [] (const BuilderContext::LoopAndLabelInfo &info) { + return info.is_loop; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + info = *lookup; + } + add_jump_to (info.continue_bb); + // No code allowed after continue. No BB starts - would be empty. } void @@ -418,9 +430,10 @@ ExprStmtBuilder::visit (HIR::UnsafeBlockExpr &expr) BuilderContext::LoopAndLabelInfo & ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) { - NodeId label = (expr.has_loop_label ()) - ? resolve_label (expr.get_loop_label ()) - : UNKNOWN_NODEID; + NodeId label + = (expr.has_loop_label ()) + ? expr.get_loop_label ().get_lifetime ().get_mappings ().get_nodeid () + : UNKNOWN_NODEID; PlaceId label_var = ctx.place_db.add_temporary (lookup_type (expr)); BasicBlockId continue_bb = new_bb (); @@ -439,7 +452,8 @@ ExprStmtBuilder::visit (HIR::LoopExpr &expr) ctx.current_bb = loop.continue_bb; (void) visit_expr (*expr.get_loop_block ()); - add_jump_to (loop.continue_bb); + if (!ctx.get_current_bb ().is_terminated ()) + add_jump_to (loop.continue_bb); ctx.current_bb = loop.break_bb; } From 4d6af2362702521b741b6f679e74963abd6b073f Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Thu, 19 Oct 2023 10:47:50 +0200 Subject: [PATCH 13/16] borrowck: Make goto explicit. gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit): Use goto. * checks/errors/borrowck/rust-bir-builder-internal.h: Explicit goto. * checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h: Explicit goto. Signed-off-by: Jakub Dupak --- .../borrowck/rust-bir-builder-expr-stmt.cc | 33 ++++++++++++------- .../borrowck/rust-bir-builder-internal.h | 7 ++++ .../borrowck/rust-bir-builder-lazyboolexpr.h | 7 ++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index 9a850facff96..367aea7c2da3 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -329,7 +329,7 @@ ExprStmtBuilder::visit (HIR::ContinueExpr &cont) rust_assert (lookup != ctx.loop_and_label_stack.rend ()); info = *lookup; } - add_jump_to (info.continue_bb); + push_goto (info.continue_bb); // No code allowed after continue. No BB starts - would be empty. } @@ -364,7 +364,7 @@ ExprStmtBuilder::visit (HIR::BreakExpr &brk) { push_assignment (info.label_var, visit_expr (*brk.get_expr ())); } - add_jump_to (info.break_bb); + push_goto (info.break_bb); // No code allowed after break. No BB starts - would be empty. } @@ -448,12 +448,12 @@ ExprStmtBuilder::visit (HIR::LoopExpr &expr) { auto loop = setup_loop (expr); - add_jump_to (loop.continue_bb); + push_goto (loop.continue_bb); ctx.current_bb = loop.continue_bb; (void) visit_expr (*expr.get_loop_block ()); if (!ctx.get_current_bb ().is_terminated ()) - add_jump_to (loop.continue_bb); + push_goto (loop.continue_bb); ctx.current_bb = loop.break_bb; } @@ -463,7 +463,7 @@ ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) { auto loop = setup_loop (expr); - add_jump_to (loop.continue_bb); + push_goto (loop.continue_bb); ctx.current_bb = loop.continue_bb; auto cond_val = visit_expr (*expr.get_predicate_expr ()); @@ -472,7 +472,7 @@ ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) ctx.current_bb = body_bb; (void) visit_expr (*expr.get_loop_block ()); - add_jump_to (loop.continue_bb); + push_goto (loop.continue_bb); ctx.current_bb = loop.break_bb; } @@ -498,6 +498,8 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr) ctx.current_bb = new_bb (); BasicBlockId then_start_block = ctx.current_bb; (void) visit_expr (*expr.get_if_block ()); + if (!ctx.get_current_bb ().is_terminated ()) + push_goto (INVALID_BB); // Resolved later. BasicBlockId then_end_block = ctx.current_bb; ctx.current_bb = new_bb (); @@ -508,7 +510,8 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr) add_jump (if_block, then_start_block); add_jump (if_block, final_block); - if (!ctx.basic_blocks[then_end_block].is_terminated ()) + auto &then_end_bb = ctx.basic_blocks[then_end_block]; + if (then_end_bb.is_goto_terminated () && then_end_bb.successors.empty ()) add_jump (then_end_block, final_block); } @@ -523,11 +526,15 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) ctx.current_bb = new_bb (); auto then_res = visit_expr (*expr.get_if_block ()); push_assignment (result, then_res); + if (!ctx.get_current_bb ().is_terminated ()) + push_goto (INVALID_BB); // Resolved later. BasicBlockId then_block = ctx.current_bb; ctx.current_bb = new_bb (); auto else_res = visit_expr (*expr.get_else_block ()); push_assignment (result, else_res); + if (!ctx.get_current_bb ().is_terminated ()) + push_goto (INVALID_BB); // Resolved later. BasicBlockId else_block = ctx.current_bb; ctx.current_bb = new_bb (); @@ -538,9 +545,12 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) add_jump (if_block, then_block); add_jump (if_block, else_block); - if (!ctx.basic_blocks[then_block].is_terminated ()) + auto &then_bb = ctx.basic_blocks[then_block]; + if (then_bb.is_goto_terminated () && then_bb.successors.empty ()) add_jump (then_block, final_block); - if (!ctx.basic_blocks[else_block].is_terminated ()) + + auto &else_bb = ctx.basic_blocks[else_block]; + if (else_bb.is_goto_terminated () && else_bb.successors.empty ()) add_jump (else_block, final_block); } void @@ -652,8 +662,8 @@ ExprStmtBuilder::visit (HIR::LetStmt &stmt) } else { - // TODO - rust_sorry_at (stmt.get_locus (), "pattern matching in let statements"); + rust_sorry_at (stmt.get_locus (), "pattern matching in let statements " + "without initializer is not supported"); } } @@ -662,5 +672,6 @@ ExprStmtBuilder::visit (HIR::ExprStmt &stmt) { (void) visit_expr (*stmt.get_expr ()); } + } // namespace BIR } // namespace Rust \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h index ef48cba7b80b..663b6ad7fae6 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -225,6 +225,13 @@ class AbstractBuilder ctx.get_current_bb ().successors.end (), destinations); } + void push_goto (BasicBlockId bb) + { + ctx.get_current_bb ().statements.emplace_back (Node::Kind::GOTO); + if (bb != INVALID_BB) // INVALID_BB means the goto will be resolved later. + ctx.get_current_bb ().successors.push_back (bb); + } + void push_storage_dead (PlaceId place) { ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_DEAD, diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h index 4ec87d9c71e6..eb6a5efc1cfe 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h @@ -48,12 +48,12 @@ class LazyBooleanExprBuilder : public AbstractBuilder, visit (expr); push_assignment (return_place, translated); auto final_bb = new_bb (); - add_jump_to (final_bb); + push_goto (final_bb); ctx.current_bb = short_circuit_bb; translated = ctx.place_db.get_constant (lookup_type (expr)); push_assignment (return_place, translated); - add_jump_to (final_bb); + push_goto (final_bb); ctx.current_bb = final_bb; return return_place; @@ -63,8 +63,7 @@ class LazyBooleanExprBuilder : public AbstractBuilder, void visit (HIR::LazyBooleanExpr &expr) override { expr.get_lhs ()->accept_vis (*this); - push_switch (translated); - add_jump_to (short_circuit_bb); + push_switch (translated, {short_circuit_bb}); start_new_subsequent_bb (); expr.get_rhs ()->accept_vis (*this); From a46aec1d5b9d5497edd6104ec19dbd0d6b47ffee Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Wed, 18 Oct 2023 23:11:20 +0200 Subject: [PATCH 14/16] borrowck: Docs gcc/rust/ChangeLog: * checks/errors/borrowck/bir-design-notes.md: New file. Signed-off-by: Jakub Dupak --- .../errors/borrowck/bir-design-notes.md | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 gcc/rust/checks/errors/borrowck/bir-design-notes.md diff --git a/gcc/rust/checks/errors/borrowck/bir-design-notes.md b/gcc/rust/checks/errors/borrowck/bir-design-notes.md new file mode 100644 index 000000000000..ad622cd72285 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/bir-design-notes.md @@ -0,0 +1,188 @@ +# Borrow-checker IR (BIR) design notes + +## Design goals + +Rust GCC project aims to use the [Polonius project](https://github.com/rust-lang/polonius) as its borrow-checker. +Polonius operates on a set of [facts](https://github.com/rust-lang/polonius/blob/master/polonius-engine/src/facts.rs) about the program to determine +the actual lifetimes of borrows. +As Polonius's primary analysis is location sensitive, the facts are tied to the program's control flow graph (CFG). +Unlike rustc, which has +its +own three address language specific representation called [MIR](https://rustc-dev-guide.rust-lang.org/mir/index.html), gccrs uses gcc's AST based +representation GENERIC. +GIMPLE (the three address IR) is generated from GENERIC inside GCC and can carry no language-specific information. +Therefore, we +need to generate our own representation of the program's CFG. +Since gccrs has no intention of having a MIR-like IR, the BIR is not to be used for +code generation. +Therefore, BIR carries only minimal information that is necessary for the borrow-checker and for BIR debugging. + +Since BIR is in fact a dead branch of the compilation pipeline, the only way to verify its generations is through manual inspection. +To have some frame of reference for testing, BIR build and dump are carefully designed to resemble the textual dump of rustc's MIR as much as +possible. +This includes the style of the output, numbering of locals and order of basic blocks (when possible). + +## BIR Dump Example + +An example program calculating the i-th fibonacci number: + +```rust + +fn fib(i: usize) -> i32 { + if i == 0 || i == 1 { + 1 + } else { + fib(i - 1) + fib(i - 2) + } +} +``` + +Here is an example of BIR dump (note: this needs to be updated regularly): + +``` +fn fib(_1: usize) -> i32 { + let _0: i32; + let _2: i32; + let _3: bool; + let _4: bool; + let _5: bool; + let _6: usize; + let _7: i32; + let _8: usize; + let _9: i32; + let _10: i32; + + bb0: { + _4 = Operator(_1, const usize); + switchInt(_4) -> [bb1, bb2]; + } + + bb1: { + _3 = const bool; + goto -> bb3; + } + + bb2: { + _5 = Operator(_1, const usize); + _3 = _5; + goto -> bb3; + } + + bb3: { + switchInt(_3) -> [bb4, bb7]; + } + + bb4: { + _2 = const i32; + goto -> bb8; + } + + bb5: { + _6 = Operator(_1, const usize); + _7 = Call(fib)(_6, ) -> [bb6]; + } + + bb6: { + _8 = Operator(_1, const usize); + _9 = Call(fib)(_8, ) -> [bb7]; + } + + bb7: { + _10 = Operator(_7, _9); + _2 = _10; + goto -> bb8; + } + + bb8: { + _0 = _2; + return; + } +} + + +``` + +The dump consists of: + +- A function header with arguments: `fn fib(_1: usize) -> i32 { ... }`. +- Declaration of locals: `let _0: i32;`, where `_0` is the return value (even if it is of the unit type). Arguments are not listed here, they are + listed in the function header. +- A list of basic blocks: `bb0: { ... }`. The basic block name is the `bb` prefix followed by a number. +- Each basic block consists of a list of BIR nodes (instructions). Instruction can be either assigned to a local (place) or be a statement. + Instructions take locals (places) as arguments. +- Each basic block is terminated with a control flow instruction followed by a list of destinations: + - `goto -> bb3;` - a goto instruction with a single destination. + - `switchInt(_3) -> [bb4, bb7];` - a switch instruction with multiple destinations. + - `return;` - a return instruction with no destinations. + - `Call(fib)(_6, ) -> [bb6];` - a call instruction with a single destination. This section is prepared for panic handling. + +## BIR Structure + +BIR structure is defined in `gcc/rust/checks/errors/borrowck/rust-bir.h`. It is heavily inspired by rustc's MIR. The main difference is that BIR +drastically reduces the amount of information carried to only borrow-checking relevant information. + +As borrow-checking is performed on each function independently, BIR represents a single function (`struct Function`). A `Function` consists of a list +of basic blocks, list of arguments (for dump only) and place database, which keeps track of locals. + +### Basic Blocks + +A basic block is identified by its index in the function's basic block list. It contains a list of BIR nodes (instructions) and a list of successor +basic block indices in CFG. + +### BIR Nodes (Instructions) + +BIR nodes are of three categories: + +- An assignment of an expression to a local (place). +- A control flow operation (switch, return). +- A special node (not executable) node, which carries additional information for borrow-checking (`StorageDead`, `StorageLive`). + +#### Expressions + +Expressions represent the executable parts of the rust code. Many different Rust contracts are represented by a single expression, as only data (and +lifetime) flow needs to be tracked. + +- `InitializerExpr` represents any kind of struct initialization. It can be either explicit (struct expression) or implicit (range expression, + e.g. `0..=5`). +- `Operator` represents any kind of operation, except the following, where special information is needed either for borrow-checking or for + better debugging. +- `BorrowExpr` represents a borrow operation. +- `AssignmentExpr` holds a place for a node of assignment (i.e., no operation is done on the place, it is just assigned). +- `CallExpr` represents a function call. + - For functions, the callable is represented by a constant place (see below). (E.i. all calls use the same constant place.) + - For closures and function pointers, the callable is represented by a (non-constant) place. + +### Places + +Places are defined in `gcc/rust/checks/errors/borrowck/rust-bir-place.h`. + +Places represent locals (variables), their field, and constants. They are identified by their index (`PlaceId`) in the function's place database. For +better dump correspondence to MIR, constants use a different index range. + +Non-constant places are created according to Polonius path [documentation](https://rust-lang.github.io/polonius/rules/atoms.html). The following +grammar describes +possible path elements: + +``` +Path = Variable + | Path "." Field // field access + | Path "[" "]" // index + | "*" Path +``` + +It is important to highlight that different fields are assigned to different places; however, all indices are assigned to the same place. +Also, to match the output of rustc. +In dump, paths contain at most one dereference and are split otherwise. +Same paths always result in the same place. + +Variables are identified by `AST` `NodeId`. Fields indexes are taken from `TyTy` types. + +Each place holds indices to its next relatives (in the path tree), `TyTy` type, lifetime and information whether the type can be copies or it needs to +be moved. Not that unlike rustc, we copy any time we can (for simplicity), while rustc prefers to move if possible (only a single copy is held). + +## BIR Builders + +There are multiple builders (visitor classes) for BIR based on what context is needed in them. +provides the entry point that handles function parameters and return values, and it creates the BIR main unit `Function`. +`rust-bir-internal.h` provides abstract builder classes with common helper methods for all builder and for expression builders. +Specific builders are then defined for expressions+statements, lazy boolean expressions, patterns, and struct initialization. \ No newline at end of file From c29c90095ce3bc44628dc80bcb2b6db76e2be93f Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Tue, 24 Oct 2023 08:32:20 +0200 Subject: [PATCH 15/16] borrowck: Dev notes gcc/rust/ChangeLog: * checks/errors/borrowck/dev-notes.md: New file. Signed-off-by: Jakub Dupak --- gcc/rust/checks/errors/borrowck/dev-notes.md | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 gcc/rust/checks/errors/borrowck/dev-notes.md diff --git a/gcc/rust/checks/errors/borrowck/dev-notes.md b/gcc/rust/checks/errors/borrowck/dev-notes.md new file mode 100644 index 000000000000..2e170e761026 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/dev-notes.md @@ -0,0 +1,40 @@ +# Borrow-checker Development Notes + +## Testing BIR building + +There is no way to test BIR building directly, since it is a dead branch of the compilation pipeline. +The only way to verify its generations is through manual inspection. +The best way to inspect the BIR is to compare it with rustc's MIR. + +The following command will compile a rust file into a library and dump its MIR: + +```shell +rustc --crate-type=lib -A dead_code -A unused -Z dump-mir="" +``` + +The MIR dump directory `mir_dump` contains a dump before and after each MIR pass. +We are interested in the one used for borrow-checking, which is called `..002-000.analysis.after.mir`. + +BIR dump is emitted to a `bir_dump` directory. With the following naming scheme: `..bir.dump`. + +At this point, MIR dump contains helper constructions that BIR does not contain yet (like storage live/dead annotations). To remove them from the MIR dump, run the following command: + +```shell +awk -i inplace '!/^\s*(\/\/|StorageLive|StorageDead|FakeRead)/' mir_dump/* +``` + +To get the BIR dump into a similar format, run the following command: + +```shell +./crab1 -frust-incomplete-and-experimental-compiler-do-not-use -frust-borrowcheck -frust-dump-bir -frust-compile-until=compilation +``` + + +## TODO + +- scope handling, cleanup +- switch coercions to adjustments from typechecking +- operator overloading +- match selection +- let without an initializer +- lifetime parameters \ No newline at end of file From 0798bc526c83426d38bd04032c2c39665f541edd Mon Sep 17 00:00:00 2001 From: Jakub Dupak Date: Thu, 19 Oct 2023 15:26:35 +0200 Subject: [PATCH 16/16] borrowck: Refactor and BIR improvements gcc/rust/ChangeLog: * checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::setup_loop): Move. (ExprStmtBuilder::get_label_ctx): Move. (ExprStmtBuilder::get_unnamed_loop_ctx): Moved. (ExprStmtBuilder::visit): BIR improvements. * checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Refactor. * checks/errors/borrowck/rust-bir-builder-internal.h (class LifetimeResolver): Refactor. (struct BuilderContext): Move.Refactor. (optional_from_ptr): Map on null ptr. * checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h (class LazyBooleanExprBuilder): Refactor. * checks/errors/borrowck/rust-bir-builder-pattern.h: Refactor. * checks/errors/borrowck/rust-bir-builder-struct.h (class StructBuilder): Refactor. * checks/errors/borrowck/rust-bir-builder.h: Refactor. * checks/errors/borrowck/rust-bir-dump.cc (Dump::go): Refactor. (Dump::visit): Refactor. (Dump::visit_place): Refactor. (Dump::visit_move_place): Refactor. (Dump::visit_lifetime): Refactor. * checks/errors/borrowck/rust-bir-dump.h: Refactor. * checks/errors/borrowck/rust-bir-place.h: Refactor. Signed-off-by: Jakub Dupak --- .../borrowck/rust-bir-builder-expr-stmt.cc | 285 ++++++++-------- .../borrowck/rust-bir-builder-expr-stmt.h | 28 +- .../borrowck/rust-bir-builder-internal.h | 319 ++++++++++-------- .../borrowck/rust-bir-builder-lazyboolexpr.h | 89 +++-- .../borrowck/rust-bir-builder-pattern.h | 36 +- .../errors/borrowck/rust-bir-builder-struct.h | 22 +- .../checks/errors/borrowck/rust-bir-builder.h | 10 +- .../checks/errors/borrowck/rust-bir-dump.cc | 57 ++-- .../checks/errors/borrowck/rust-bir-dump.h | 5 +- .../checks/errors/borrowck/rust-bir-place.h | 72 ++-- 10 files changed, 508 insertions(+), 415 deletions(-) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index 367aea7c2da3..96bc738964ed 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -25,6 +25,48 @@ namespace Rust { namespace BIR { +using LoopAndLabelCtx = BuilderContext::LoopAndLabelCtx; + +BuilderContext::LoopAndLabelCtx & +ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) +{ + NodeId label + = (expr.has_loop_label ()) + ? expr.get_loop_label ().get_lifetime ().get_mappings ().get_nodeid () + : UNKNOWN_NODEID; + PlaceId label_var = take_or_create_return_place (lookup_type (expr)); + + BasicBlockId continue_bb = new_bb (); + BasicBlockId break_bb = new_bb (); + ctx.loop_and_label_stack.push_back ( + {true, label, label_var, break_bb, continue_bb}); + return ctx.loop_and_label_stack.back (); +} + +BuilderContext::LoopAndLabelCtx & +ExprStmtBuilder::get_label_ctx (HIR::Lifetime &label) +{ + NodeId label_id = resolve_label (label); + auto lookup = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [label_id] (LoopAndLabelCtx &info) { + return info.label == label_id; + }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + return *lookup; +} + +LoopAndLabelCtx & +ExprStmtBuilder::get_unnamed_loop_ctx () +{ + auto lookup + = std::find_if (ctx.loop_and_label_stack.rbegin (), + ctx.loop_and_label_stack.rend (), + [] (LoopAndLabelCtx &info) { return info.is_loop; }); + rust_assert (lookup != ctx.loop_and_label_stack.rend ()); + return *lookup; +} + void ExprStmtBuilder::visit (HIR::ClosureExpr &expr) { @@ -34,8 +76,9 @@ ExprStmtBuilder::visit (HIR::ClosureExpr &expr) { captures.push_back (ctx.place_db.lookup_variable (capture)); } + make_args (captures); - // Not a coercion site. + // Note: Not a coercion site for captures. return_expr (new InitializerExpr (std::move (captures)), lookup_type (expr)); } @@ -45,6 +88,7 @@ ExprStmtBuilder::visit (HIR::StructExprStructFields &fields) auto struct_ty = lookup_type (fields)->as ()->get_variants ().at (0); auto init_values = StructBuilder (ctx, struct_ty).build (fields); + make_args (init_values); return_expr (new InitializerExpr (std::move (init_values)), lookup_type (fields)); } @@ -59,7 +103,7 @@ ExprStmtBuilder::visit (HIR::StructExprStruct &expr) void ExprStmtBuilder::visit (HIR::LiteralExpr &expr) { - // Different literal values of the same type are not distinguished. + // Different literal values of the same type are not distinguished in BIR. return_place (ctx.place_db.get_constant (lookup_type (expr))); } @@ -74,12 +118,14 @@ void ExprStmtBuilder::visit (HIR::DereferenceExpr &expr) { auto operand = visit_expr (*expr.get_expr ()); - return_place (operand); + return_place (ctx.place_db.lookup_or_add_path (Place::DEREF, + lookup_type (expr), operand)); } void ExprStmtBuilder::visit (HIR::ErrorPropagationExpr &expr) { + // TODO: desugar in AST->HIR rust_sorry_at (expr.get_locus (), "error propagation is not supported"); } @@ -87,7 +133,7 @@ void ExprStmtBuilder::visit (HIR::NegationExpr &expr) { PlaceId operand = visit_expr (*expr.get_expr ()); - return_expr (new Operator<1> ({operand}), lookup_type (expr)); + return_expr (new Operator<1> ({make_arg (operand)}), lookup_type (expr)); } void @@ -95,7 +141,8 @@ ExprStmtBuilder::visit (HIR::ArithmeticOrLogicalExpr &expr) { PlaceId lhs = visit_expr (*expr.get_lhs ()); PlaceId rhs = visit_expr (*expr.get_rhs ()); - return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr)); + return_expr (new Operator<2> ({make_arg (lhs), make_arg (rhs)}), + lookup_type (expr)); } void @@ -103,19 +150,23 @@ ExprStmtBuilder::visit (HIR::ComparisonExpr &expr) { PlaceId lhs = visit_expr (*expr.get_lhs ()); PlaceId rhs = visit_expr (*expr.get_rhs ()); - return_expr (new Operator<2> ({lhs, rhs}), lookup_type (expr)); + return_expr (new Operator<2> ({make_arg (lhs), make_arg (rhs)}), + lookup_type (expr)); } void ExprStmtBuilder::visit (HIR::LazyBooleanExpr &expr) { - return_place (LazyBooleanExprBuilder (ctx).build (expr)); + return_place (LazyBooleanExprBuilder (ctx, take_or_create_return_place ( + lookup_type (expr))) + .build (expr)); } void ExprStmtBuilder::visit (HIR::TypeCastExpr &expr) { - return_place (visit_expr (*expr.get_expr ())); + auto operand = visit_expr (*expr.get_expr ()); + return_expr (new Operator<1> ({operand}), lookup_type (expr)); } void @@ -132,34 +183,31 @@ ExprStmtBuilder::visit (HIR::CompoundAssignmentExpr &expr) auto lhs = visit_expr (*expr.get_lhs ()); auto rhs = visit_expr (*expr.get_rhs ()); push_assignment (lhs, new Operator<2> ({lhs, rhs})); - // TODO: (philip) nicer unit? - return_place (ctx.place_db.get_constant (lookup_type (expr))); } void ExprStmtBuilder::visit (HIR::GroupedExpr &expr) { - // Uses accept_vis directly to avoid creating n new temporary. - expr.get_expr_in_parens ()->accept_vis (*this); + return_place (visit_expr (*expr.get_expr_in_parens ())); } void ExprStmtBuilder::visit (HIR::ArrayExpr &expr) { - switch (expr.get_internal_elements ()->get_array_expr_type ()) + auto &elems = expr.get_internal_elements (); + switch (elems->get_array_expr_type ()) { case HIR::ArrayElems::VALUES: { - auto init_values = visit_list ((static_cast ( - expr.get_internal_elements ().get ())) - ->get_values ()); + auto &elem_vals = (static_cast (*elems)); + auto init_values = visit_list (elem_vals.get_values ()); + make_args (init_values); return_expr (new InitializerExpr (std::move (init_values)), lookup_type (expr)); break; } case HIR::ArrayElems::COPIED: { - auto init = visit_expr (*(static_cast ( - expr.get_internal_elements ().get ())) - ->get_elem_to_copy ()); + auto &elem_copied = (static_cast (*elems)); + auto init = visit_expr (*elem_copied.get_elem_to_copy ()); return_expr (new InitializerExpr ({init}), lookup_type (expr)); break; } @@ -171,7 +219,7 @@ ExprStmtBuilder::visit (HIR::ArrayIndexExpr &expr) { auto lhs = visit_expr (*expr.get_array_expr ()); auto rhs = visit_expr (*expr.get_index_expr ()); - // The Index is not tracked in BIR. + // The index is not tracked in BIR. (void) rhs; return_place ( ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (expr), lhs)); @@ -181,14 +229,6 @@ void ExprStmtBuilder::visit (HIR::TupleExpr &expr) { std::vector init_values = visit_list (expr.get_tuple_elems ()); - if (std::any_of (init_values.begin (), init_values.end (), - [this] (PlaceId id) { - return ctx.place_db[id].lifetime.has_lifetime (); - })) - { - ctx.place_db[expr_return_place].lifetime - = {ctx.lifetime_interner.get_anonymous ()}; - } return_expr (new InitializerExpr (std::move (init_values)), lookup_type (expr)); } @@ -208,7 +248,7 @@ ExprStmtBuilder::visit (HIR::CallExpr &expr) PlaceId fn = visit_expr (*expr.get_fnexpr ()); std::vector arguments = visit_list (expr.get_arguments ()); - TyTy::BaseType *call_type = ctx.place_db[fn].tyty; + auto *call_type = ctx.place_db[fn].tyty; if (auto fn_type = call_type->try_as ()) { for (size_t i = 0; i < fn_type->get_params ().size (); ++i) @@ -233,6 +273,10 @@ ExprStmtBuilder::visit (HIR::CallExpr &expr) true); } +void +ExprStmtBuilder::visit (HIR::MethodCallExpr &expr) +{} + void ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) { @@ -242,12 +286,12 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) auto adt = type->as (); rust_assert (!adt->is_enum ()); rust_assert (adt->number_of_variants () == 1); - TyTy::VariantDef *variant = adt->get_variants ().at (0); + auto struct_ty = adt->get_variants ().at (0); TyTy::StructFieldType *field_ty = nullptr; size_t field_index = 0; - bool ok = variant->lookup_field (expr.get_field_name ().as_string (), - &field_ty, &field_index); + bool ok = struct_ty->lookup_field (expr.get_field_name ().as_string (), + &field_ty, &field_index); rust_assert (ok); return_place (ctx.place_db.lookup_or_add_path (Place::FIELD, @@ -258,114 +302,71 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) void ExprStmtBuilder::visit (HIR::BlockExpr &block) { - BasicBlockId end_bb; - if (block.has_label ()) { - end_bb = new_bb (); NodeId label = block.get_label ().get_lifetime ().get_mappings ().get_nodeid (); - PlaceId label_var = ctx.place_db.add_temporary (lookup_type (block)); - ctx.loop_and_label_stack.push_back ({false, label, label_var, end_bb, 0}); + PlaceId label_var = take_or_create_return_place (lookup_type (block)); + ctx.loop_and_label_stack.push_back ( + {false, label, label_var, new_bb (), INVALID_BB}); } + // Eliminates dead code after break, continue, return. bool unreachable = false; for (auto &stmt : block.get_statements ()) { - if (unreachable) - break; stmt->accept_vis (*this); if (ctx.get_current_bb ().is_terminated ()) - unreachable = true; + { + unreachable = true; + break; + } } if (block.has_label ()) { - auto label_info = ctx.loop_and_label_stack.back (); + auto block_ctx = ctx.loop_and_label_stack.back (); if (block.has_expr () && !unreachable) { - push_assignment (label_info.label_var, + push_assignment (block_ctx.label_var, visit_expr (*block.get_final_expr ())); } if (!ctx.get_current_bb ().is_terminated ()) { - add_jump_to (end_bb); + push_goto (block_ctx.break_bb); } - ctx.current_bb = end_bb; + ctx.current_bb = block_ctx.break_bb; ctx.loop_and_label_stack.pop_back (); - return_place (label_info.label_var); + return_place (block_ctx.label_var); } else if (block.has_expr () && !unreachable) { - return_place (visit_expr (*block.get_final_expr ())); + return_place (visit_expr (*block.get_final_expr (), + take_or_create_return_place ( + lookup_type (*block.get_final_expr ())))); } } void ExprStmtBuilder::visit (HIR::ContinueExpr &cont) { - BuilderContext::LoopAndLabelInfo info; - if (cont.has_label ()) - { - NodeId label = resolve_label (cont.get_label ()); - auto lookup - = std::find_if (ctx.loop_and_label_stack.rbegin (), - ctx.loop_and_label_stack.rend (), - [label] (const BuilderContext::LoopAndLabelInfo &info) { - return info.label == label; - }); - rust_assert (lookup != ctx.loop_and_label_stack.rend ()); - info = *lookup; - } - else - { - auto lookup - = std::find_if (ctx.loop_and_label_stack.rbegin (), - ctx.loop_and_label_stack.rend (), - [] (const BuilderContext::LoopAndLabelInfo &info) { - return info.is_loop; - }); - rust_assert (lookup != ctx.loop_and_label_stack.rend ()); - info = *lookup; - } + LoopAndLabelCtx info = cont.has_label () ? get_label_ctx (cont.get_label ()) + : get_unnamed_loop_ctx (); push_goto (info.continue_bb); - // No code allowed after continue. No BB starts - would be empty. + // No code allowed after continue. Handled in BlockExpr. } void ExprStmtBuilder::visit (HIR::BreakExpr &brk) { - BuilderContext::LoopAndLabelInfo info; - if (brk.has_label ()) - { - NodeId label = resolve_label (brk.get_label ()); - auto lookup - = std::find_if (ctx.loop_and_label_stack.rbegin (), - ctx.loop_and_label_stack.rend (), - [label] (const BuilderContext::LoopAndLabelInfo &info) { - return info.label == label; - }); - rust_assert (lookup != ctx.loop_and_label_stack.rend ()); - info = *lookup; - } - else - { - auto lookup - = std::find_if (ctx.loop_and_label_stack.rbegin (), - ctx.loop_and_label_stack.rend (), - [] (const BuilderContext::LoopAndLabelInfo &info) { - return info.is_loop; - }); - rust_assert (lookup != ctx.loop_and_label_stack.rend ()); - info = *lookup; - } + LoopAndLabelCtx info = brk.has_label () ? get_label_ctx (brk.get_label ()) + : get_unnamed_loop_ctx (); if (brk.has_break_expr ()) - { - push_assignment (info.label_var, visit_expr (*brk.get_expr ())); - } + push_assignment (info.label_var, visit_expr (*brk.get_expr ())); + push_goto (info.break_bb); - // No code allowed after break. No BB starts - would be empty. + // No code allowed after continue. Handled in BlockExpr. } void @@ -427,22 +428,6 @@ ExprStmtBuilder::visit (HIR::UnsafeBlockExpr &expr) rust_sorry_at (expr.get_locus (), "unsafe blocks are not supported"); } -BuilderContext::LoopAndLabelInfo & -ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) -{ - NodeId label - = (expr.has_loop_label ()) - ? expr.get_loop_label ().get_lifetime ().get_mappings ().get_nodeid () - : UNKNOWN_NODEID; - PlaceId label_var = ctx.place_db.add_temporary (lookup_type (expr)); - - BasicBlockId continue_bb = new_bb (); - BasicBlockId break_bb = new_bb (); - ctx.loop_and_label_stack.push_back ( - {true, label, label_var, break_bb, continue_bb}); - return ctx.loop_and_label_stack.back (); -} - void ExprStmtBuilder::visit (HIR::LoopExpr &expr) { @@ -518,40 +503,40 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr) void ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) { - PlaceId result = ctx.place_db.add_temporary (lookup_type (expr)); + push_switch (make_arg (visit_expr (*expr.get_if_condition ()))); + BasicBlockId if_end_bb = ctx.current_bb; - push_switch (visit_expr (*expr.get_if_condition ())); - BasicBlockId if_block = ctx.current_bb; + PlaceId result = take_or_create_return_place (lookup_type (expr)); ctx.current_bb = new_bb (); - auto then_res = visit_expr (*expr.get_if_block ()); - push_assignment (result, then_res); + BasicBlockId then_start_bb = ctx.current_bb; + (void) visit_expr (*expr.get_if_block (), result); if (!ctx.get_current_bb ().is_terminated ()) push_goto (INVALID_BB); // Resolved later. - BasicBlockId then_block = ctx.current_bb; + BasicBlockId then_end_bb = ctx.current_bb; ctx.current_bb = new_bb (); - auto else_res = visit_expr (*expr.get_else_block ()); - push_assignment (result, else_res); + BasicBlockId else_start_bb = ctx.current_bb; + (void) visit_expr (*expr.get_else_block (), result); if (!ctx.get_current_bb ().is_terminated ()) push_goto (INVALID_BB); // Resolved later. - BasicBlockId else_block = ctx.current_bb; + BasicBlockId else_end_bb = ctx.current_bb; ctx.current_bb = new_bb (); - BasicBlockId final_block = ctx.current_bb; + BasicBlockId final_start_bb = ctx.current_bb; return_place (result); // Jumps are added at the end to match rustc MIR order for easier comparison. - add_jump (if_block, then_block); - add_jump (if_block, else_block); + add_jump (if_end_bb, then_start_bb); + add_jump (if_end_bb, else_start_bb); - auto &then_bb = ctx.basic_blocks[then_block]; + auto &then_bb = ctx.basic_blocks[then_end_bb]; if (then_bb.is_goto_terminated () && then_bb.successors.empty ()) - add_jump (then_block, final_block); + add_jump (then_end_bb, final_start_bb); - auto &else_bb = ctx.basic_blocks[else_block]; + auto &else_bb = ctx.basic_blocks[else_end_bb]; if (else_bb.is_goto_terminated () && else_bb.successors.empty ()) - add_jump (else_block, final_block); + add_jump (else_end_bb, final_start_bb); } void ExprStmtBuilder::visit (HIR::IfLetExpr &expr) @@ -566,6 +551,7 @@ ExprStmtBuilder::visit (HIR::IfLetExprConseqElse &expr) void ExprStmtBuilder::visit (HIR::MatchExpr &expr) { + rust_sorry_at (expr.get_locus (), "match expressions are not supported"); // // TODO // expr.get_scrutinee_expr ()->accept_vis (*this); // PlaceId scrutinee = translated; @@ -621,44 +607,47 @@ ExprStmtBuilder::visit (HIR::AsyncBlockExpr &expr) void ExprStmtBuilder::visit (HIR::QualifiedPathInExpression &expr) { - PlaceId result; // Note: Type is only stored for the expr, not the segment. - bool ok = resolve_variable (expr.get_final_segment (), result); - rust_assert (ok); + PlaceId result + = resolve_variable_or_fn (expr.get_final_segment (), lookup_type (expr)); return_place (result); } void ExprStmtBuilder::visit (HIR::PathInExpression &expr) { - PlaceId result; // Note: Type is only stored for the expr, not the segment. - bool ok = resolve_variable (expr.get_final_segment (), result); - rust_assert (ok); + PlaceId result + = resolve_variable_or_fn (expr.get_final_segment (), lookup_type (expr)); return_place (result); } void ExprStmtBuilder::visit (HIR::LetStmt &stmt) { - if (stmt.has_init_expr ()) - { - auto init = visit_expr (*stmt.get_init_expr ()); - PatternBindingBuilder (ctx, init, stmt.get_type ().get ()) - .go (*stmt.get_pattern ()); - } - else if (stmt.get_pattern ()->get_pattern_type () == HIR::Pattern::IDENTIFIER) + if (stmt.get_pattern ()->get_pattern_type () == HIR::Pattern::IDENTIFIER) { + // Only if a pattern is just an identifier, no destructuring is needed. + // Hoverer PatternBindingBuilder cannot change existing temporary + // (init expr is evaluated before pattern binding) into a + // variable, so it would emit extra assignment. auto var = declare_variable (stmt.get_pattern ()->get_mappings ()); auto &var_place = ctx.place_db[var]; if (var_place.tyty->get_kind () == TyTy::REF) { - auto p_type = tl::optional ( - static_cast (stmt.get_type ().get ())); var_place.lifetime = ctx.lookup_lifetime ( - p_type.map (&HIR::ReferenceType::get_lifetime)); + optional_from_ptr ( + static_cast (stmt.get_type ().get ())) + .map (&HIR::ReferenceType::get_lifetime)); } - return; + if (stmt.has_init_expr ()) + (void) visit_expr (*stmt.get_init_expr (), var); + } + else if (stmt.has_init_expr ()) + { + auto init = visit_expr (*stmt.get_init_expr ()); + PatternBindingBuilder (ctx, init, stmt.get_type ().get ()) + .go (*stmt.get_pattern ()); } else { diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h index e5707c31f8d3..1352965b725e 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h @@ -25,14 +25,21 @@ namespace Rust { namespace BIR { +/** + * Compiles expressions into a BIR place. + * See AbstractExprBuilder for API usage docs (mainly `return_place` and + * `return_expr`). + */ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor { - PlaceId expr_return_place = INVALID_PLACE; - public: explicit ExprStmtBuilder (BuilderContext &ctx) : AbstractExprBuilder (ctx) {} - PlaceId build (HIR::Expr &expr) { return visit_expr (expr); } + /** Entry point. */ + PlaceId build (HIR::Expr &expr, PlaceId place = INVALID_PLACE) + { + return visit_expr (expr, place); + } private: template @@ -46,17 +53,19 @@ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor return result; } - BuilderContext::LoopAndLabelInfo &setup_loop (HIR::BaseLoopExpr &expr); + /** Common infrastructure for loops. */ + BuilderContext::LoopAndLabelCtx &setup_loop (HIR::BaseLoopExpr &expr); + + BuilderContext::LoopAndLabelCtx &get_label_ctx (HIR::Lifetime &label); + BuilderContext::LoopAndLabelCtx &get_unnamed_loop_ctx (); protected: // Expr - // TODO: test when compiles void visit (HIR::ClosureExpr &expr) override; void visit (HIR::StructExprStructFields &fields) override; void visit (HIR::StructExprStruct &expr) override; void visit (HIR::LiteralExpr &expr) override; void visit (HIR::BorrowExpr &expr) override; void visit (HIR::DereferenceExpr &expr) override; - // TODO: desugar in AST->HIR void visit (HIR::ErrorPropagationExpr &expr) override; void visit (HIR::NegationExpr &expr) override; void visit (HIR::ArithmeticOrLogicalExpr &expr) override; @@ -71,7 +80,7 @@ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor void visit (HIR::TupleExpr &expr) override; void visit (HIR::TupleIndexExpr &expr) override; void visit (HIR::CallExpr &expr) override; - void visit (HIR::MethodCallExpr &expr) override {} + void visit (HIR::MethodCallExpr &expr) override; void visit (HIR::FieldAccessExpr &expr) override; void visit (HIR::BlockExpr &block) override; void visit (HIR::ContinueExpr &cont) override; @@ -96,7 +105,7 @@ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor void visit (HIR::AwaitExpr &expr) override; void visit (HIR::AsyncBlockExpr &expr) override; - // Nodes not containing executable code. Nothing to do. +protected: // Nodes not containing executable code. Nothing to do. void visit (HIR::QualifiedPathInExpression &expr) override; void visit (HIR::PathInExpression &expr) override; @@ -119,9 +128,8 @@ class ExprStmtBuilder : public AbstractExprBuilder, public HIR::HIRStmtVisitor void visit (HIR::ExprStmt &stmt) override; -protected: // Unused +protected: // Ignored. // Only executable code of a single function/method is translated. - // All other items are ignored. void visit (HIR::EnumItemTuple &tuple) override {} void visit (HIR::EnumItemStruct &a_struct) override {} void visit (HIR::EnumItem &item) override {} diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h index 663b6ad7fae6..f33eb0752446 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -31,31 +31,35 @@ namespace Rust { namespace BIR { -class LifetimeResolver +/** Holds the context of BIR building so that it can be shared/passed between + * different builders. */ +struct BuilderContext { - using Index = uint32_t; - using Value = std::string; + class LifetimeResolver + { + using Index = uint32_t; + using Value = std::string; - Index next_index = FIRST_NORMAL_LIFETIME_ID; - std::unordered_map value_to_index; + Index next_index = FIRST_NORMAL_LIFETIME_ID; + std::unordered_map value_to_index; -public: - Index intern (const Value &value) - { - auto found = value_to_index.find (value); - if (found != value_to_index.end ()) - { - return found->second; - } - value_to_index.emplace (value, next_index); - return next_index++; - } - Index get_anonymous () { return next_index++; } -}; + public: + Index resolve (const Value &value) + { + auto found = value_to_index.find (value); + if (found != value_to_index.end ()) + { + return found->second; + } + value_to_index.emplace (value, next_index); + return next_index++; + } -struct BuilderContext -{ - struct LoopAndLabelInfo + /** Returns a new anonymous lifetime. */ + Index get_anonymous () { return next_index++; } + }; + + struct LoopAndLabelCtx { bool is_loop; // Loop or labelled block NodeId label; // UNKNOWN_NODEID if no label (loop) @@ -63,19 +67,20 @@ struct BuilderContext BasicBlockId break_bb; BasicBlockId continue_bb; // Only valid for loops - LoopAndLabelInfo (bool is_loop = false, NodeId label = UNKNOWN_NODEID, - PlaceId label_var = INVALID_PLACE, - BasicBlockId break_bb = INVALID_BB, - BasicBlockId continue_bb = INVALID_BB) + LoopAndLabelCtx (bool is_loop = false, NodeId label = UNKNOWN_NODEID, + PlaceId label_var = INVALID_PLACE, + BasicBlockId break_bb = INVALID_BB, + BasicBlockId continue_bb = INVALID_BB) : is_loop (is_loop), label (label), label_var (label_var), break_bb (break_bb), continue_bb (continue_bb) {} }; - // Context + // External context. Resolver::TypeCheckContext &tyctx; Resolver::Resolver &resolver; + // BIR output std::vector basic_blocks; size_t current_bb = 0; @@ -85,6 +90,7 @@ struct BuilderContext */ PlaceDB place_db; LifetimeResolver lifetime_interner; + // Used for cleaner dump. std::vector arguments; /** * Since labels can be used to return values, we need to reserve a place for @@ -92,7 +98,8 @@ struct BuilderContext */ std::unordered_map label_place_map; - std::vector loop_and_label_stack; + /** Context for current situation (loop, label, etc.) */ + std::vector loop_and_label_stack; public: BuilderContext () @@ -111,7 +118,7 @@ struct BuilderContext switch (lifetime->get_lifetime_type ()) { case AST::Lifetime::NAMED: { - return {lifetime_interner.intern (lifetime->get_name ())}; + return {lifetime_interner.resolve (lifetime->get_name ())}; } case AST::Lifetime::STATIC: { return STATIC_LIFETIME; @@ -125,9 +132,9 @@ struct BuilderContext rust_unreachable (); }; - const LoopAndLabelInfo &lookup_label (NodeId label) + const LoopAndLabelCtx &lookup_label (NodeId label) { - auto label_match = [label] (const LoopAndLabelInfo &info) { + auto label_match = [label] (const LoopAndLabelCtx &info) { return info.label != UNKNOWN_NODEID && info.label == label; }; @@ -138,67 +145,39 @@ struct BuilderContext } }; -// Common infrastructure for building BIR from HIR. +/** Common infrastructure for building BIR from HIR. */ class AbstractBuilder { protected: BuilderContext &ctx; - // This emulates the return value of the visitor, to be able to use the - // current visitor infrastructure, where the return value is forced to be - // void. + /** + * This emulates the return value of the visitor, to be able to use the + * current visitor infrastructure, where the return value is forced to be + * void. + */ PlaceId translated = INVALID_PLACE; protected: explicit AbstractBuilder (BuilderContext &ctx) : ctx (ctx) {} - WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Expr &expr) - { - return expr.get_mappings ().get_nodeid (); - } - - WARN_UNUSED_RESULT static NodeId get_nodeid (HIR::Pattern &pattern) - { - return pattern.get_mappings ().get_nodeid (); - } - - template - WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (T &pattern) const - { - return lookup_type (pattern.get_mappings ().get_hirid ()); - } - - WARN_UNUSED_RESULT TyTy::BaseType *lookup_type (HirId hirid) const - { - TyTy::BaseType *type = nullptr; - bool ok = ctx.tyctx.lookup_type (hirid, &type); - rust_assert (ok); - rust_assert (type != nullptr); - return type; - } - PlaceId declare_variable (const Analysis::NodeMapping &node) { - const NodeId nodeid = node.get_nodeid (); - const HirId hirid = node.get_hirid (); - - // In debug mode check that the variable is not already declared. - rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE); - - return ctx.place_db.add_variable (nodeid, lookup_type (hirid)); + return declare_variable (node, lookup_type (node.get_hirid ())); } PlaceId declare_variable (const Analysis::NodeMapping &node, - TyTy::BaseType *type) + TyTy::BaseType *ty) { const NodeId nodeid = node.get_nodeid (); - // In debug mode check that the variable is not already declared. + // In debug mode, check that the variable is not already declared. rust_assert (ctx.place_db.lookup_variable (nodeid) == INVALID_PLACE); - return ctx.place_db.add_variable (nodeid, type); + return ctx.place_db.add_variable (nodeid, ty); } +protected: // Helpers to add BIR nodes void push_assignment (PlaceId lhs, AbstractExpr *rhs) { ctx.get_current_bb ().statements.emplace_back (lhs, rhs); @@ -219,8 +198,8 @@ class AbstractBuilder void push_switch (PlaceId switch_val, std::initializer_list destinations = {}) { - ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, - switch_val); + auto copy = make_arg (switch_val); + ctx.get_current_bb ().statements.emplace_back (Node::Kind::SWITCH, copy); ctx.get_current_bb ().successors.insert ( ctx.get_current_bb ().successors.end (), destinations); } @@ -232,25 +211,40 @@ class AbstractBuilder ctx.get_current_bb ().successors.push_back (bb); } - void push_storage_dead (PlaceId place) + PlaceId declare_rvalue (PlaceId place) + { + ctx.place_db[place].is_rvalue = true; + return place; + } + + void declare_rvalues (std::vector &places) + { + for (auto &place : places) + declare_rvalue (place); + } + + PlaceId make_arg (PlaceId arg) { - ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_DEAD, - place); + auto copy = ctx.place_db.into_rvalue (arg); + if (copy != arg) + push_assignment (copy, arg); + return copy; } - void push_storage_live (PlaceId place) + void make_args (std::vector &args) { - ctx.get_current_bb ().statements.emplace_back (Node::Kind::STORAGE_LIVE, - place); + std::transform (args.begin (), args.end (), args.begin (), + [this] (PlaceId arg) { return make_arg (arg); }); } +protected: // CFG helpers BasicBlockId new_bb () { ctx.basic_blocks.emplace_back (); return ctx.basic_blocks.size () - 1; } - BasicBlockId start_new_subsequent_bb () + BasicBlockId start_new_consecutive_bb () { BasicBlockId bb = new_bb (); ctx.get_current_bb ().successors.emplace_back (bb); @@ -265,7 +259,22 @@ class AbstractBuilder void add_jump_to (BasicBlockId bb) { add_jump (ctx.current_bb, bb); } -protected: +protected: // HIR resolution helpers + template + [[nodiscard]] TyTy::BaseType *lookup_type (T &hir_node) const + { + return lookup_type (hir_node.get_mappings ().get_hirid ()); + } + + [[nodiscard]] TyTy::BaseType *lookup_type (HirId hirid) const + { + TyTy::BaseType *type = nullptr; + bool ok = ctx.tyctx.lookup_type (hirid, &type); + rust_assert (ok); + rust_assert (type != nullptr); + return type; + } + template NodeId resolve_label (T &expr) { NodeId resolved_label; @@ -276,42 +285,30 @@ class AbstractBuilder return resolved_label; } - template - bool resolve_variable (T &variable, PlaceId &resolved_variable) + template PlaceId resolve_variable (T &variable) { NodeId variable_id; - if (!ctx.resolver.lookup_resolved_name ( - variable.get_mappings ().get_nodeid (), &variable_id)) - { - // TODO: should this be assert? (should be caught by typecheck) - rust_error_at (variable.get_locus (), "unresolved variable"); - return false; - } - resolved_variable = ctx.place_db.lookup_variable (variable_id); - return true; + bool ok = ctx.resolver.lookup_resolved_name ( + variable.get_mappings ().get_nodeid (), &variable_id); + rust_assert (ok); + return ctx.place_db.lookup_variable (variable_id); } - bool find_block_ctx (NodeId label, BuilderContext::LoopAndLabelInfo &block) + template + PlaceId resolve_variable_or_fn (T &variable, TyTy::BaseType *ty) { - if (ctx.loop_and_label_stack.empty ()) - return false; - if (label == UNKNOWN_NODEID) - { - block = ctx.loop_and_label_stack.back (); - return true; - } - auto found - = std::find_if (ctx.loop_and_label_stack.rbegin (), - ctx.loop_and_label_stack.rend (), - [&label] (const BuilderContext::LoopAndLabelInfo &block) { - return block.label == label; - }); - if (found == ctx.loop_and_label_stack.rend ()) - return false; - block = *found; - return true; + // Unlike variables, + // functions do not have to be declared in PlaceDB before use. + NodeId variable_id; + bool ok = ctx.resolver.lookup_resolved_name ( + variable.get_mappings ().get_nodeid (), &variable_id); + rust_assert (ok); + return ctx.place_db.lookup_or_add_variable (variable_id, + (ty) ? ty + : lookup_type (variable)); } +protected: // Implicit conversions. /** * Performs implicit coercions on the `translated` place defined for a * coercion site. @@ -362,7 +359,6 @@ class AbstractBuilder return ty; } - /** For operator */ void autoref () { if (ctx.place_db[translated].tyty->get_kind () != TyTy::REF) @@ -380,67 +376,108 @@ class AbstractExprBuilder : public AbstractBuilder, public HIR::HIRExpressionVisitor { protected: - // Exactly one of this and `translated` is used by each visitor. - AbstractExpr *expr_return_expr = nullptr; + /** + * Optional place for the result of the evaluated expression. + * Valid if value is not `INVALID_PLACE`. + * Used when return place must be created by caller (return for if-else). + */ + PlaceId expr_return_place = INVALID_PLACE; protected: - explicit AbstractExprBuilder (BuilderContext &ctx) : AbstractBuilder (ctx) {} + explicit AbstractExprBuilder (BuilderContext &ctx, + PlaceId expr_return_place = INVALID_PLACE) + : AbstractBuilder (ctx), expr_return_place (expr_return_place) + {} - PlaceId visit_expr (HIR::Expr &expr) + /** + * Wrapper that provides return value based API inside a visitor which has to + * use global state to pass the data around. + * @param dst_place Place to assign the produced value to, optionally + * allocated by the caller. + * */ + PlaceId visit_expr (HIR::Expr &expr, PlaceId dst_place = INVALID_PLACE) { - // Reset return places. + // Save to support proper recursion. + auto saved = expr_return_place; + expr_return_place = dst_place; translated = INVALID_PLACE; - expr_return_expr = nullptr; expr.accept_vis (*this); - if (translated != INVALID_PLACE) + expr_return_place = saved; + auto result = translated; + translated = INVALID_PLACE; + return result; + } + + /** + * Create a return value of a subexpression, which produces an expression. + * Use `return_place` for subexpression that only produce a place (look it up) + * to avoid needless assignments. + * + * @param can_panic mark that expression can panic to insert jump to cleanup. + */ + void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, + bool can_panic = false) + { + if (expr_return_place != INVALID_PLACE) { - auto result = translated; - translated = INVALID_PLACE; - return result; + push_assignment (expr_return_place, expr); } - else if (expr_return_expr != nullptr) + else { - // Only allocate temporary, if needed. - push_tmp_assignment (expr_return_expr, lookup_type (expr)); - expr_return_expr = nullptr; - return translated; + push_tmp_assignment (expr, ty); } - else + + if (can_panic) { - return ctx.place_db.get_constant (lookup_type (expr)); + start_new_consecutive_bb (); } } - void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, - bool can_panic = false) + /** Mark place to be a result of processed subexpression. */ + void return_place (PlaceId place) { - rust_assert (expr_return_expr == nullptr); - if (can_panic) + if (expr_return_place != INVALID_PLACE) { - push_tmp_assignment (expr, ty); - // TODO, cleanup? - start_new_subsequent_bb (); + // Return place is already allocated, no need to defer assignment. + push_assignment (expr_return_place, place); } else { - translated = INVALID_PLACE; - expr_return_expr = expr; + translated = place; } } - void return_place (PlaceId place) + /** Explicitly return a unit value. Expression produces no value. */ + void return_unit (HIR::Expr &expr) { - expr_return_expr = nullptr; - translated = place; + translated = ctx.place_db.get_constant (lookup_type (expr)); } - void return_unit (HIR::Expr &expr) + PlaceId take_or_create_return_place (TyTy::BaseType *type) { - expr_return_expr = nullptr; - translated = ctx.place_db.get_constant (lookup_type (expr)); + auto result = (expr_return_place != INVALID_PLACE) + ? expr_return_place + : ctx.place_db.add_temporary (type); + expr_return_place = INVALID_PLACE; + return result; } }; +/** + * Helper to convert a pointer to an optional. Maps nullptr to nullopt. + * Optionals are mainly used here to provide monadic operations (map) over + * possibly null pointers. + */ +template +tl::optional +optional_from_ptr (T ptr) +{ + if (ptr != nullptr) + return {ptr}; + else + return tl::nullopt; +} + } // namespace BIR } // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h index eb6a5efc1cfe..440549eba29b 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h @@ -30,29 +30,28 @@ namespace BIR { * Special builder is needed to store short-circuiting context for directly * nested lazy boolean expressions. */ -class LazyBooleanExprBuilder : public AbstractBuilder, - public HIR::HIRExpressionVisitor +class LazyBooleanExprBuilder : public AbstractExprBuilder { BasicBlockId short_circuit_bb; public: - explicit LazyBooleanExprBuilder (BuilderContext &ctx) - : AbstractBuilder (ctx), short_circuit_bb (0) + explicit LazyBooleanExprBuilder (BuilderContext &ctx, + PlaceId expr_return_place = INVALID_PLACE) + : AbstractExprBuilder (ctx, expr_return_place), short_circuit_bb (0) {} PlaceId build (HIR::LazyBooleanExpr &expr) { - PlaceId return_place = ctx.place_db.add_temporary (lookup_type (expr)); + PlaceId return_place = take_or_create_return_place (lookup_type (expr)); short_circuit_bb = new_bb (); - visit (expr); - push_assignment (return_place, translated); + push_assignment (return_place, visit_expr (expr)); auto final_bb = new_bb (); push_goto (final_bb); ctx.current_bb = short_circuit_bb; - translated = ctx.place_db.get_constant (lookup_type (expr)); - push_assignment (return_place, translated); + push_assignment (return_place, + ctx.place_db.get_constant (lookup_type (expr))); push_goto (final_bb); ctx.current_bb = final_bb; @@ -62,11 +61,11 @@ class LazyBooleanExprBuilder : public AbstractBuilder, protected: void visit (HIR::LazyBooleanExpr &expr) override { - expr.get_lhs ()->accept_vis (*this); - push_switch (translated, {short_circuit_bb}); + auto lhs = visit_expr (*expr.get_lhs ()); + push_switch (make_arg (lhs), {short_circuit_bb}); - start_new_subsequent_bb (); - expr.get_rhs ()->accept_vis (*this); + start_new_consecutive_bb (); + return_place (visit_expr (*expr.get_rhs ())); } void visit (HIR::GroupedExpr &expr) override { @@ -77,15 +76,15 @@ class LazyBooleanExprBuilder : public AbstractBuilder, public: void visit (HIR::QualifiedPathInExpression &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::PathInExpression &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ClosureExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::StructExprStructFields &fields) override { @@ -97,119 +96,119 @@ class LazyBooleanExprBuilder : public AbstractBuilder, } void visit (HIR::LiteralExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::BorrowExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::DereferenceExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ErrorPropagationExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::NegationExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ArithmeticOrLogicalExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ComparisonExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::TypeCastExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::AssignmentExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::CompoundAssignmentExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ArrayExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::ArrayIndexExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::TupleExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::TupleIndexExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::CallExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::MethodCallExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::FieldAccessExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::BlockExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::UnsafeBlockExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::LoopExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::WhileLoopExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::WhileLetLoopExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::IfExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::IfExprConseqElse &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::IfLetExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::IfLetExprConseqElse &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::MatchExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::AwaitExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } void visit (HIR::AsyncBlockExpr &expr) override { - translated = ExprStmtBuilder (ctx).build (expr); + return_place (ExprStmtBuilder (ctx).build (expr)); } protected: // Illegal at this position. diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h index 0b4c83eca844..0596264afc51 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h @@ -23,16 +23,20 @@ namespace Rust { namespace BIR { -// Compiles binding of values into newly created variables. -// Used in let, match arm, and function parameter patterns. + +/** + * Compiles binding of values into newly created variables. + * Used in let, match arm, and function parameter patterns. + */ class PatternBindingBuilder : protected AbstractBuilder, public HIR::HIRPatternVisitor { + /** Value of initialization expression. */ PlaceId init; - // This is where lifetime annotations are stored. + /** This is where lifetime annotations are stored. */ tl::optional type; - // Emulates recursive stack saving and restoring inside a visitor. + /** Emulates recursive stack saving and restoring inside a visitor. */ class SavedState { PatternBindingBuilder *builder; @@ -55,21 +59,26 @@ class PatternBindingBuilder : protected AbstractBuilder, public: PatternBindingBuilder (BuilderContext &ctx, PlaceId init, HIR::Type *type) - : AbstractBuilder (ctx), init (init), - type (type ? tl::optional (type) : tl::nullopt) + : AbstractBuilder (ctx), init (init), type (optional_from_ptr (type)) {} void go (HIR::Pattern &pattern) { pattern.accept_vis (*this); } - void visit_identifier (const Analysis::NodeMapping &node, bool is_ref) + void visit_identifier (const Analysis::NodeMapping &node, bool is_ref, + bool is_mut = false) { - translated = declare_variable (node); if (is_ref) { + translated = declare_variable ( + node, new TyTy::ReferenceType (node.get_hirid (), + TyTy::TyVar (node.get_hirid ()), + (is_mut) ? Mutability::Mut + : Mutability::Imm)); push_assignment (translated, new BorrowExpr (init)); } else { + translated = declare_variable (node); push_assignment (translated, init); } auto &init_place = ctx.place_db[init]; @@ -87,8 +96,10 @@ class PatternBindingBuilder : protected AbstractBuilder, { // Top-level identifiers are resolved directly to avoid useless temporary // (for cleaner BIR). - visit_identifier (pattern.get_mappings (), pattern.get_is_ref ()); + visit_identifier (pattern.get_mappings (), pattern.get_is_ref (), + pattern.is_mut ()); } + void visit (HIR::ReferencePattern &pattern) override { SavedState saved (this); @@ -104,6 +115,7 @@ class PatternBindingBuilder : protected AbstractBuilder, = ctx.lookup_lifetime (ref_type.map (&HIR::ReferenceType::get_lifetime)); pattern.get_referenced_pattern ()->accept_vis (*this); } + void visit (HIR::SlicePattern &pattern) override { SavedState saved (this); @@ -119,11 +131,13 @@ class PatternBindingBuilder : protected AbstractBuilder, item->accept_vis (*this); } } + void visit (HIR::AltPattern &pattern) override { rust_sorry_at (pattern.get_locus (), "borrow-checking of alt patterns is not yet implemented"); } + void visit (HIR::StructPattern &pattern) override { SavedState saved (this); @@ -181,12 +195,14 @@ class PatternBindingBuilder : protected AbstractBuilder, field_ty->get_field_type (), saved.init, field_index); visit_identifier (ident_field->get_mappings (), - ident_field->get_has_ref ()); + ident_field->get_has_ref (), + ident_field->is_mut ()); break; } } } } + void visit_tuple_fields (std::vector> &fields, SavedState &saved, size_t &index) { diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h index fa2f5965af68..7df54a4880aa 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h @@ -29,6 +29,7 @@ namespace BIR { class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor { TyTy::VariantDef *struct_ty; + /** Output of the builder. */ std::vector init_values; public: @@ -47,34 +48,33 @@ class StructBuilder : public AbstractBuilder, public HIR::HIRFullVisitor void visit (HIR::StructExprFieldIdentifier &field) override { - resolve_variable (field, translated); - handle_named_field (field); + handle_named_field (field, resolve_variable (field)); } void visit (HIR::StructExprFieldIdentifierValue &field) override { - translated = ExprStmtBuilder (ctx).build (*field.get_value ()); - handle_named_field (field); + auto value = ExprStmtBuilder (ctx).build (*field.get_value ()); + handle_named_field (field, value); } void visit (HIR::StructExprFieldIndexValue &field) override { - translated = ExprStmtBuilder (ctx).build (*field.get_value ()); - coercion_site (translated, + auto value = ExprStmtBuilder (ctx).build (*field.get_value ()); + coercion_site (value, struct_ty->get_field_at_index (field.get_tuple_index ()) ->get_field_type ()); - init_values.push_back (translated); + init_values.push_back (value); } private: - template void handle_named_field (T &field) + template void handle_named_field (T &field, PlaceId value) { size_t field_index; TyTy::StructFieldType *field_type; bool ok = struct_ty->lookup_field (field.get_field_name ().as_string (), &field_type, &field_index); rust_assert (ok); - rust_assert (translated != INVALID_PLACE); - coercion_site (translated, field_type->get_field_type ()); - init_values.push_back (translated); + rust_assert (value != INVALID_PLACE); + coercion_site (value, field_type->get_field_type ()); + init_values.push_back (value); } protected: diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h index 322d00d280e5..177b65558899 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h @@ -74,12 +74,18 @@ class Builder : public AbstractBuilder void handle_body (HIR::BlockExpr &body) { translated = ExprStmtBuilder (ctx).build (body); - if (body.has_expr ()) + if (body.has_expr () && !lookup_type (body)->is_unit ()) { push_assignment (RETURN_VALUE_PLACE, translated); ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN); } - } + else if (!ctx.get_current_bb ().is_terminated ()) + { + push_assignment (RETURN_VALUE_PLACE, + ctx.place_db.get_constant (lookup_type (body))); + ctx.get_current_bb ().statements.emplace_back (Node::Kind::RETURN); + } + }; }; } // namespace BIR diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index 66870ddeb565..23aa7c6b078b 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -114,24 +114,27 @@ Dump::go (bool enable_simplify_cfg) stream << "fn " << name << "("; print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) { stream << "_" << place_map[place_id] << ": " - << get_tyty_name (place_db[place_id].tyty); + << get_tyty_name (func.place_db[place_id].tyty); }); - stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty) + stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty) << " {\n"; - for (PlaceId id = FIRST_VARIABLE_PLACE; id < place_db.size (); ++id) + // Print locals declaration. + for (PlaceId id = FIRST_VARIABLE_PLACE; id < func.place_db.size (); ++id) { - const Place &place = place_db[id]; + const Place &place = func.place_db[id]; if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY) { if (std::find (func.arguments.begin (), func.arguments.end (), id) != func.arguments.end ()) continue; - stream << indentation << "let _" << place_map[id] << ": " - << get_tyty_name (place_db[id].tyty) << ";\n"; + stream << indentation << "let _"; + stream << place_map[id] << ": " + << get_tyty_name (func.place_db[id].tyty) << ";\n"; } } + // Print BBs. for (node_bb = 0; node_bb < func.basic_blocks.size (); ++node_bb) { if (bb_fold_map[node_bb] != node_bb) @@ -141,6 +144,8 @@ Dump::go (bool enable_simplify_cfg) && func.basic_blocks[node_bb].successors.empty ()) continue; + bb_terminated = false; + BasicBlock &bb = func.basic_blocks[node_bb]; stream << "\n"; stream << indentation << "bb" << bb_fold_map[node_bb] << ": {\n"; @@ -150,10 +155,15 @@ Dump::go (bool enable_simplify_cfg) visit (stmt); stream << ";\n"; } + if (!bb_terminated) + { + stream << indentation << indentation << "goto -> bb" + << bb_fold_map[bb.successors.at (0)] << ";\n"; + } stream << indentation << "}\n"; } - stream << "}\n\n"; + stream << "}\n"; } void Dump::visit (Node &node) @@ -162,7 +172,8 @@ Dump::visit (Node &node) switch (node.get_kind ()) { case Node::Kind::ASSIGNMENT: { - stream << "_" << place_map[node.get_place ()] << " = "; + visit_place (node.get_place ()); + stream << " = "; node.get_expr ().accept_vis (*this); break; } @@ -175,13 +186,16 @@ Dump::visit (Node &node) stream << "bb" << bb_fold_map[succ]; }); stream << "]"; + bb_terminated = true; break; case Node::Kind::RETURN: stream << "return"; + bb_terminated = true; break; case Node::Kind::GOTO: stream << "goto -> bb" << bb_fold_map[func.basic_blocks[node_bb].successors.at (0)]; + bb_terminated = true; break; case Node::Kind::STORAGE_DEAD: stream << "StorageDead("; @@ -200,7 +214,7 @@ Dump::visit (Node &node) void Dump::visit_place (PlaceId place_id) { - const Place &place = place_db[place_id]; + const Place &place = func.place_db[place_id]; switch (place.kind) { case Place::TEMPORARY: @@ -237,8 +251,8 @@ Dump::visit_place (PlaceId place_id) void Dump::visit_move_place (PlaceId place_id) { - const Place &place = place_db[place_id]; - if (!place.is_copy) + const Place &place = func.place_db[place_id]; + if (place.is_rvalue || !place.is_copy) stream << "move "; visit_place (place_id); } @@ -254,7 +268,7 @@ Dump::visit (BorrowExpr &expr) void Dump::visit_lifetime (PlaceId place_id) { - const Place &place = place_db[place_id]; + const Place &place = func.place_db[place_id]; if (place.lifetime.has_lifetime ()) { if (place.lifetime.id == STATIC_LIFETIME_ID) @@ -279,7 +293,7 @@ Dump::visit (CallExpr &expr) { stream << "Call("; if (auto fn_type - = place_db[expr.get_callable ()].tyty->try_as ()) + = func.place_db[expr.get_callable ()].tyty->try_as ()) { stream << fn_type->get_identifier (); } @@ -288,18 +302,17 @@ Dump::visit (CallExpr &expr) visit_move_place (expr.get_callable ()); } stream << ")("; - for (auto &place : expr.get_arguments ()) - { - visit_move_place (place); - stream << ", "; - } + print_comma_separated (stream, expr.get_arguments (), + [this] (PlaceId place_id) { + visit_move_place (place_id); + }); stream << ") -> ["; - print_comma_separated (stream, - func.basic_blocks[bb_fold_map[node_bb]].successors, - [this] (const BasicBlockId &dst) { - stream << "bb" << bb_fold_map[dst]; + print_comma_separated (stream, func.basic_blocks[node_bb].successors, + [this] (BasicBlockId succ) { + stream << "bb" << bb_fold_map[succ]; }); stream << "]"; + bb_terminated = true; } void diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h index edf7d1ea1ec3..1efc0ea2bf46 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -28,10 +28,10 @@ namespace Rust { namespace BIR { +/** Prints the BIR to a stream in a format resembling rustc's MIR. */ class Dump : public Visitor { std::ostream &stream; - const PlaceDB &place_db; Function &func; const std::string &name; @@ -40,10 +40,11 @@ class Dump : public Visitor PlaceId node_place = INVALID_PLACE; BasicBlockId node_bb = INVALID_BB; + bool bb_terminated = false; public: Dump (std::ostream &os, Function &func, const std::string &name) - : stream (os), place_db (func.place_db), func (func), name (name), + : stream (os), func (func), name (name), bb_fold_map (func.basic_blocks.size ()), place_map (func.place_db.size ()) {} void go (bool enable_simplify_cfg = false); diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-place.h b/gcc/rust/checks/errors/borrowck/rust-bir-place.h index ce32f9262ceb..66b131dee1a7 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-place.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-place.h @@ -148,30 +148,9 @@ class PlaceDB 0); } - PlaceId lookup_variable (NodeId id) - { - PlaceId current = FIRST_VARIABLE_PLACE; - - while (current != places.size ()) - { - if (places[current].kind == Place::VARIABLE - && places[current].variable_or_field_index == id) - return current; - current++; - } - return INVALID_PLACE; - }; - - PlaceId add_temporary (TyTy::BaseType *tyty) - { - return add_place ( - {Place::TEMPORARY, 0, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty}, - 0); - } - - WARN_UNUSED_RESULT PlaceId lookup_or_add_path (Place::Kind kind, - TyTy::BaseType *tyty, - PlaceId parent, size_t id = 0) + [[nodiscard]] PlaceId lookup_or_add_path (Place::Kind kind, + TyTy::BaseType *tyty, + PlaceId parent, size_t id = 0) { PlaceId current = 0; if (parent < places.size ()) @@ -193,6 +172,13 @@ class PlaceDB current); } + PlaceId add_temporary (TyTy::BaseType *tyty) + { + return add_place ( + {Place::TEMPORARY, 0, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty}, + 0); + } + PlaceId get_constant (TyTy::BaseType *tyty) { auto lookup = constants_lookup.find (tyty); @@ -206,6 +192,44 @@ class PlaceDB return places.size () - 1; } + PlaceId lookup_variable (NodeId id) + { + PlaceId current = FIRST_VARIABLE_PLACE; + + while (current != places.size ()) + { + if (places[current].kind == Place::VARIABLE + && places[current].variable_or_field_index == id) + return current; + current++; + } + return INVALID_PLACE; + }; + + PlaceId lookup_or_add_variable (NodeId id, TyTy::BaseType *tyty) + { + auto lookup = lookup_variable (id); + if (lookup != INVALID_PLACE) + return lookup; + places.push_back ( + {Place::VARIABLE, id, {}, is_type_copy (tyty), false, NO_LIFETIME, tyty}); + return places.size () - 1; + }; + + PlaceId into_rvalue (PlaceId place) + { + if (places[place].is_rvalue || places[place].kind == Place::CONSTANT + || places[place].tyty->get_kind () == TyTy::REF) + return place; + return add_place ({Place::TEMPORARY, + 0, + {}, + places[place].is_copy, + true, + NO_LIFETIME, + places[place].tyty}); + } + private: static bool is_type_copy (TyTy::BaseType *ty) {