From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 29E113AA8CBA; Wed, 8 Jun 2022 11:58:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 29E113AA8CBA Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Thomas Schwinge To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] Add overflow checking on LiteralExpression X-Act-Checkin: gcc X-Git-Author: Philip Herron X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: 69f6be3ee483c9895b4b5187a44b3e1c8be2ba63 X-Git-Newrev: 507dbac06d845af87e488dcb1f66390b4ca12de3 Message-Id: <20220608115836.29E113AA8CBA@sourceware.org> Date: Wed, 8 Jun 2022 11:58:36 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Jun 2022 11:58:36 -0000 https://gcc.gnu.org/g:507dbac06d845af87e488dcb1f66390b4ca12de3 commit 507dbac06d845af87e488dcb1f66390b4ca12de3 Author: Philip Herron Date: Wed Jan 5 18:31:03 2022 +0000 Add overflow checking on LiteralExpression This checks that the literal value is within the bounds of their respective types. I have ommited code fixing the other issue in the bug report that overflow/max_val integers should be saturated to infinity when cast to REAL_TYPE's this seems like something we really should have documentation to reference in the code as to why this is the correct Rust behaviour. Addresses #635 Diff: --- gcc/rust/backend/rust-compile-expr.cc | 207 ++++++++++++++++++++++++++ gcc/rust/backend/rust-compile-expr.h | 166 ++++++--------------- gcc/rust/hir/tree/rust-hir-expr.h | 3 +- gcc/rust/typecheck/rust-hir-const-fold.h | 6 +- gcc/rust/typecheck/rust-hir-type-check-expr.h | 10 +- gcc/rust/typecheck/rust-tycheck-dump.h | 2 +- gcc/rust/typecheck/rust-tyty-cast.h | 2 +- gcc/testsuite/rust/compile/issue-635-1.rs | 5 + gcc/testsuite/rust/compile/issue-635-2.rs | 5 + 9 files changed, 278 insertions(+), 128 deletions(-) diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index b77a4d5d57c..0307b2145ae 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -27,6 +27,8 @@ #include "rust-compile-pattern.h" #include "fold-const.h" +#include "realmpfr.h" +#include "convert.h" namespace Rust { namespace Compile { @@ -893,5 +895,210 @@ CompileExpr::resolve_operator_overload ( nullptr, expr.get_locus ()); } +tree +CompileExpr::compile_bool_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::BOOL); + + const auto literal_value = expr.get_literal (); + bool bval = literal_value.as_string ().compare ("true") == 0; + return ctx->get_backend ()->boolean_constant_expression (bval); +} + +tree +CompileExpr::compile_integer_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::INT); + const auto literal_value = expr.get_literal (); + + tree type = TyTyResolveCompile::compile (ctx, tyty); + rust_assert (TREE_CODE (type) == INTEGER_TYPE); + + mpz_t ival; + if (mpz_init_set_str (ival, literal_value.as_string ().c_str (), 10) != 0) + { + rust_error_at (expr.get_locus (), "bad number in literal"); + return error_mark_node; + } + + mpz_t type_min; + mpz_t type_max; + mpz_init (type_min); + mpz_init (type_max); + get_type_static_bounds (type, type_min, type_max); + + if (mpz_cmp (ival, type_min) < 0 || mpz_cmp (ival, type_max) > 0) + { + rust_error_at (expr.get_locus (), + "integer overflows the respective type %<%s%>", + tyty->get_name ().c_str ()); + return error_mark_node; + } + return double_int_to_tree (type, mpz_get_double_int (type, ival, true)); +} + +tree +CompileExpr::compile_float_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::FLOAT); + const auto literal_value = expr.get_literal (); + + mpfr_t fval; + if (mpfr_init_set_str (fval, literal_value.as_string ().c_str (), 10, + MPFR_RNDN) + != 0) + { + rust_error_at (expr.get_locus (), "bad number in literal"); + return error_mark_node; + } + + tree type = TyTyResolveCompile::compile (ctx, tyty); + + // taken from: + // see go/gofrontend/expressions.cc:check_float_type + mpfr_exp_t exp = mpfr_get_exp (fval); + bool real_value_overflow = exp > TYPE_PRECISION (type); + + REAL_VALUE_TYPE r1; + real_from_mpfr (&r1, fval, type, GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert (&r2, TYPE_MODE (type), &r1); + + tree real_value = build_real (type, r2); + if (TREE_OVERFLOW (real_value) || real_value_overflow) + { + rust_error_at (expr.get_locus (), + "decimal overflows the respective type %<%s%>", + tyty->get_name ().c_str ()); + return error_mark_node; + } + + return real_value; +} + +tree +CompileExpr::compile_char_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::CHAR); + const auto literal_value = expr.get_literal (); + + // FIXME needs wchar_t + char c = literal_value.as_string ().c_str ()[0]; + return ctx->get_backend ()->wchar_constant_expression (c); +} + +tree +CompileExpr::compile_byte_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::BYTE); + const auto literal_value = expr.get_literal (); + + tree type = TyTyResolveCompile::compile (ctx, tyty); + char c = literal_value.as_string ().c_str ()[0]; + return build_int_cst (type, c); +} + +tree +CompileExpr::compile_string_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::STRING); + const auto literal_value = expr.get_literal (); + + auto base = ctx->get_backend ()->string_constant_expression ( + literal_value.as_string ()); + return ctx->get_backend ()->address_expression (base, expr.get_locus ()); +} + +tree +CompileExpr::compile_byte_string_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty) +{ + rust_assert (expr.get_lit_type () == HIR::Literal::BYTE_STRING); + + // the type here is &[ty; capacity] + rust_assert (tyty->get_kind () == TyTy::TypeKind::REF); + const auto ref_tyty = static_cast (tyty); + auto base_tyty = ref_tyty->get_base (); + rust_assert (base_tyty->get_kind () == TyTy::TypeKind::ARRAY); + auto array_tyty = static_cast (base_tyty); + + std::string value_str = expr.get_literal ().as_string (); + std::vector vals; + std::vector indexes; + for (size_t i = 0; i < value_str.size (); i++) + { + char b = value_str.at (i); + tree bb = ctx->get_backend ()->char_constant_expression (b); + vals.push_back (bb); + indexes.push_back (i); + } + + tree array_type = TyTyResolveCompile::compile (ctx, array_tyty); + tree constructed + = ctx->get_backend ()->array_constructor_expression (array_type, indexes, + vals, + expr.get_locus ()); + + return ctx->get_backend ()->address_expression (constructed, + expr.get_locus ()); +} + +tree +CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree, + Location location) +{ + if (type_to_cast_to == error_mark_node || expr_tree == error_mark_node + || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + if (ctx->get_backend ()->type_size (type_to_cast_to) == 0 + || TREE_TYPE (expr_tree) == void_type_node) + { + // Do not convert zero-sized types. + return expr_tree; + } + else if (TREE_CODE (type_to_cast_to) == INTEGER_TYPE) + { + tree cast = fold (convert_to_integer (type_to_cast_to, expr_tree)); + // FIXME check for TREE_OVERFLOW? + return cast; + } + else if (TREE_CODE (type_to_cast_to) == REAL_TYPE) + { + tree cast = fold (convert_to_real (type_to_cast_to, expr_tree)); + // FIXME + // We might need to check that the tree is MAX val and thusly saturate it + // to inf. we can get the bounds and check the value if its >= or <= to + // the min and max bounds + // + // https://github.com/Rust-GCC/gccrs/issues/635 + return cast; + } + else if (TREE_CODE (type_to_cast_to) == COMPLEX_TYPE) + { + return fold (convert_to_complex (type_to_cast_to, expr_tree)); + } + else if (TREE_CODE (type_to_cast_to) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE) + { + return fold (convert_to_pointer (type_to_cast_to, expr_tree)); + } + else if (TREE_CODE (type_to_cast_to) == RECORD_TYPE + || TREE_CODE (type_to_cast_to) == ARRAY_TYPE) + { + return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR, + type_to_cast_to, expr_tree); + } + + return fold_convert_loc (location.gcc_location (), type_to_cast_to, + expr_tree); +} + } // namespace Compile } // namespace Rust diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h index dc4f90937c2..2f446217683 100644 --- a/gcc/rust/backend/rust-compile-expr.h +++ b/gcc/rust/backend/rust-compile-expr.h @@ -234,132 +234,41 @@ public: void visit (HIR::LiteralExpr &expr) override { - auto literal_value = expr.get_literal (); + TyTy::BaseType *tyty = nullptr; + if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), + &tyty)) + return; + switch (expr.get_lit_type ()) { - case HIR::Literal::BOOL: { - bool bval = literal_value->as_string ().compare ("true") == 0; - translated = ctx->get_backend ()->boolean_constant_expression (bval); - } + case HIR::Literal::BOOL: + translated = compile_bool_literal (expr, tyty); return; - case HIR::Literal::INT: { - mpz_t ival; - if (mpz_init_set_str (ival, literal_value->as_string ().c_str (), 10) - != 0) - { - rust_fatal_error (expr.get_locus (), "bad number in literal"); - return; - } - - TyTy::BaseType *tyty = nullptr; - if (!ctx->get_tyctx ()->lookup_type ( - expr.get_mappings ().get_hirid (), &tyty)) - { - rust_fatal_error ( - expr.get_locus (), - "did not resolve type for this literal expr (HirId %d)", - expr.get_mappings ().get_hirid ()); - return; - } - - tree type = TyTyResolveCompile::compile (ctx, tyty); - translated - = ctx->get_backend ()->integer_constant_expression (type, ival); - } + case HIR::Literal::INT: + translated = compile_integer_literal (expr, tyty); return; - case HIR::Literal::FLOAT: { - mpfr_t fval; - if (mpfr_init_set_str (fval, literal_value->as_string ().c_str (), 10, - MPFR_RNDN) - != 0) - { - rust_fatal_error (expr.get_locus (), - "bad floating-point number in literal"); - return; - } - - TyTy::BaseType *tyty = nullptr; - if (!ctx->get_tyctx ()->lookup_type ( - expr.get_mappings ().get_hirid (), &tyty)) - { - rust_fatal_error (expr.get_locus (), - "did not resolve type for this literal expr"); - return; - } - - tree type = TyTyResolveCompile::compile (ctx, tyty); - translated - = ctx->get_backend ()->float_constant_expression (type, fval); - } + case HIR::Literal::FLOAT: + translated = compile_float_literal (expr, tyty); return; - case HIR::Literal::CHAR: { - // FIXME needs wchar_t - char c = literal_value->as_string ().c_str ()[0]; - translated = ctx->get_backend ()->wchar_constant_expression (c); - } + case HIR::Literal::CHAR: + translated = compile_char_literal (expr, tyty); return; - case HIR::Literal::BYTE: { - char c = literal_value->as_string ().c_str ()[0]; - translated = ctx->get_backend ()->char_constant_expression (c); - } + case HIR::Literal::BYTE: + translated = compile_byte_literal (expr, tyty); return; - case HIR::Literal::STRING: { - auto base = ctx->get_backend ()->string_constant_expression ( - literal_value->as_string ()); - translated - = ctx->get_backend ()->address_expression (base, expr.get_locus ()); - } + case HIR::Literal::STRING: + translated = compile_string_literal (expr, tyty); return; - case HIR::Literal::BYTE_STRING: { - TyTy::BaseType *tyty = nullptr; - if (!ctx->get_tyctx ()->lookup_type ( - expr.get_mappings ().get_hirid (), &tyty)) - { - rust_error_at (expr.get_locus (), - "did not resolve type for this byte string"); - return; - } - - // the type here is &[ty; capacity] - rust_assert (tyty->get_kind () == TyTy::TypeKind::REF); - auto ref_tyty = static_cast (tyty); - auto base_tyty = ref_tyty->get_base (); - rust_assert (base_tyty->get_kind () == TyTy::TypeKind::ARRAY); - auto array_tyty = static_cast (base_tyty); - - std::string value_str = expr.get_literal ()->as_string (); - std::vector vals; - std::vector indexes; - for (size_t i = 0; i < value_str.size (); i++) - { - char b = value_str.at (i); - tree bb = ctx->get_backend ()->char_constant_expression (b); - vals.push_back (bb); - indexes.push_back (i); - } - - tree array_type = TyTyResolveCompile::compile (ctx, array_tyty); - tree constructed = ctx->get_backend ()->array_constructor_expression ( - array_type, indexes, vals, expr.get_locus ()); - - translated - = ctx->get_backend ()->address_expression (constructed, - expr.get_locus ()); - } - return; - - default: - rust_fatal_error (expr.get_locus (), "unknown literal"); + case HIR::Literal::BYTE_STRING: + translated = compile_byte_string_literal (expr, tyty); return; } - - gcc_unreachable (); } void visit (HIR::AssignmentExpr &expr) override @@ -502,20 +411,19 @@ public: void visit (HIR::TypeCastExpr &expr) override { - TyTy::BaseType *tyty_to_cast_to = nullptr; + TyTy::BaseType *tyty = nullptr; if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), - &tyty_to_cast_to)) + &tyty)) { - translated = ctx->get_backend ()->error_expression (); + translated = error_mark_node; return; } - auto type_to_cast_to = TyTyResolveCompile::compile (ctx, tyty_to_cast_to); + auto type_to_cast_to = TyTyResolveCompile::compile (ctx, tyty); auto casted_expr = CompileExpr::Compile (expr.get_casted_expr ().get (), ctx); translated - = ctx->get_backend ()->convert_expression (type_to_cast_to, casted_expr, - expr.get_locus ()); + = type_cast_expression (type_to_cast_to, casted_expr, expr.get_locus ()); } void visit (HIR::IfExpr &expr) override @@ -1067,9 +975,33 @@ protected: HIR::OperatorExpr &expr, tree lhs, tree rhs, HIR::Expr *lhs_expr, HIR::Expr *rhs_expr); + tree compile_bool_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_integer_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_float_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_char_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_byte_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_string_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree compile_byte_string_literal (const HIR::LiteralExpr &expr, + const TyTy::BaseType *tyty); + + tree type_cast_expression (tree type_to_cast_to, tree expr, Location locus); + private: CompileExpr (Context *ctx) - : HIRCompileBase (ctx), translated (nullptr), capacity_expr (nullptr) + : HIRCompileBase (ctx), translated (error_mark_node), + capacity_expr (nullptr) {} tree translated; diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h index 678455051cf..c1495d4a501 100644 --- a/gcc/rust/hir/tree/rust-hir-expr.h +++ b/gcc/rust/hir/tree/rust-hir-expr.h @@ -95,7 +95,8 @@ public: void accept_vis (HIRFullVisitor &vis) override; - Literal *get_literal () { return &literal; } + Literal &get_literal () { return literal; } + const Literal &get_literal () const { return literal; } protected: /* Use covariance to implement clone function as returning this object rather diff --git a/gcc/rust/typecheck/rust-hir-const-fold.h b/gcc/rust/typecheck/rust-hir-const-fold.h index d1f71277754..19078165862 100644 --- a/gcc/rust/typecheck/rust-hir-const-fold.h +++ b/gcc/rust/typecheck/rust-hir-const-fold.h @@ -354,7 +354,7 @@ public: { case HIR::Literal::INT: { mpz_t ival; - if (mpz_init_set_str (ival, literal_value->as_string ().c_str (), 10) + if (mpz_init_set_str (ival, literal_value.as_string ().c_str (), 10) != 0) { rust_fatal_error (expr.get_locus (), "bad number in literal"); @@ -376,14 +376,14 @@ public: return; case HIR::Literal::BOOL: { - bool bval = literal_value->as_string ().compare ("true") == 0; + bool bval = literal_value.as_string ().compare ("true") == 0; folded = ctx->get_backend ()->boolean_constant_expression (bval); } return; case HIR::Literal::FLOAT: { mpfr_t fval; - if (mpfr_init_set_str (fval, literal_value->as_string ().c_str (), 10, + if (mpfr_init_set_str (fval, literal_value.as_string ().c_str (), 10, MPFR_RNDN) != 0) { diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h index 8de736db542..cb3aa6a5237 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-expr.h +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h @@ -604,7 +604,7 @@ public: case HIR::Literal::LitType::INT: { bool ok = false; - switch (expr.get_literal ()->get_type_hint ()) + switch (expr.get_literal ().get_type_hint ()) { case CORETYPE_I8: ok = context->lookup_builtin ("i8", &infered); @@ -639,11 +639,11 @@ public: break; case CORETYPE_F32: - expr.get_literal ()->set_lit_type (HIR::Literal::LitType::FLOAT); + expr.get_literal ().set_lit_type (HIR::Literal::LitType::FLOAT); ok = context->lookup_builtin ("f32", &infered); break; case CORETYPE_F64: - expr.get_literal ()->set_lit_type (HIR::Literal::LitType::FLOAT); + expr.get_literal ().set_lit_type (HIR::Literal::LitType::FLOAT); ok = context->lookup_builtin ("f64", &infered); break; @@ -661,7 +661,7 @@ public: case HIR::Literal::LitType::FLOAT: { bool ok = false; - switch (expr.get_literal ()->get_type_hint ()) + switch (expr.get_literal ().get_type_hint ()) { case CORETYPE_F32: ok = context->lookup_builtin ("f32", &infered); @@ -727,7 +727,7 @@ public: /* Capacity is the size of the string (number of chars). It is a constant, but for fold it to get a tree. */ std::string capacity_str - = std::to_string (expr.get_literal ()->as_string ().size ()); + = std::to_string (expr.get_literal ().as_string ().size ()); HIR::LiteralExpr literal_capacity (capacity_mapping, capacity_str, HIR::Literal::LitType::INT, PrimitiveCoreType::CORETYPE_USIZE, diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h index c7219cd31d5..33a4b11539f 100644 --- a/gcc/rust/typecheck/rust-tycheck-dump.h +++ b/gcc/rust/typecheck/rust-tycheck-dump.h @@ -141,7 +141,7 @@ public: void visit (HIR::LiteralExpr &expr) override { - dump += expr.get_literal ()->as_string () + ":" + dump += expr.get_literal ().as_string () + ":" + type_string (expr.get_mappings ()); } diff --git a/gcc/rust/typecheck/rust-tyty-cast.h b/gcc/rust/typecheck/rust-tyty-cast.h index aaa589beda3..07fedd0f878 100644 --- a/gcc/rust/typecheck/rust-tyty-cast.h +++ b/gcc/rust/typecheck/rust-tyty-cast.h @@ -444,7 +444,7 @@ public: void visit (FloatType &type) override { bool is_valid - = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::INTEGRAL) || (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT); if (is_valid) { diff --git a/gcc/testsuite/rust/compile/issue-635-1.rs b/gcc/testsuite/rust/compile/issue-635-1.rs new file mode 100644 index 00000000000..dc6a4c2eece --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-635-1.rs @@ -0,0 +1,5 @@ +// { dg-additional-options "-w" } +fn test() -> i32 { + return 10000000000000000000000000000000000000000000; + // { dg-error "integer overflows the respective type .i32." "" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/rust/compile/issue-635-2.rs b/gcc/testsuite/rust/compile/issue-635-2.rs new file mode 100644 index 00000000000..335218aa52c --- /dev/null +++ b/gcc/testsuite/rust/compile/issue-635-2.rs @@ -0,0 +1,5 @@ +// { dg-additional-options "-w" } +fn test() -> f32 { + return 10000000000000000000000000000000000000000000.0f32; + // { dg-error "decimal overflows the respective type .f32." "" { target *-*-* } .-1 } +}