Skip to content

Commit 507dbac

Browse files
committed
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
1 parent 69f6be3 commit 507dbac

File tree

9 files changed

+278
-128
lines changed

9 files changed

+278
-128
lines changed

gcc/rust/backend/rust-compile-expr.cc

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include "rust-compile-pattern.h"
2828

2929
#include "fold-const.h"
30+
#include "realmpfr.h"
31+
#include "convert.h"
3032

3133
namespace Rust {
3234
namespace Compile {
@@ -893,5 +895,210 @@ CompileExpr::resolve_operator_overload (
893895
nullptr, expr.get_locus ());
894896
}
895897

898+
tree
899+
CompileExpr::compile_bool_literal (const HIR::LiteralExpr &expr,
900+
const TyTy::BaseType *tyty)
901+
{
902+
rust_assert (expr.get_lit_type () == HIR::Literal::BOOL);
903+
904+
const auto literal_value = expr.get_literal ();
905+
bool bval = literal_value.as_string ().compare ("true") == 0;
906+
return ctx->get_backend ()->boolean_constant_expression (bval);
907+
}
908+
909+
tree
910+
CompileExpr::compile_integer_literal (const HIR::LiteralExpr &expr,
911+
const TyTy::BaseType *tyty)
912+
{
913+
rust_assert (expr.get_lit_type () == HIR::Literal::INT);
914+
const auto literal_value = expr.get_literal ();
915+
916+
tree type = TyTyResolveCompile::compile (ctx, tyty);
917+
rust_assert (TREE_CODE (type) == INTEGER_TYPE);
918+
919+
mpz_t ival;
920+
if (mpz_init_set_str (ival, literal_value.as_string ().c_str (), 10) != 0)
921+
{
922+
rust_error_at (expr.get_locus (), "bad number in literal");
923+
return error_mark_node;
924+
}
925+
926+
mpz_t type_min;
927+
mpz_t type_max;
928+
mpz_init (type_min);
929+
mpz_init (type_max);
930+
get_type_static_bounds (type, type_min, type_max);
931+
932+
if (mpz_cmp (ival, type_min) < 0 || mpz_cmp (ival, type_max) > 0)
933+
{
934+
rust_error_at (expr.get_locus (),
935+
"integer overflows the respective type %<%s%>",
936+
tyty->get_name ().c_str ());
937+
return error_mark_node;
938+
}
939+
return double_int_to_tree (type, mpz_get_double_int (type, ival, true));
940+
}
941+
942+
tree
943+
CompileExpr::compile_float_literal (const HIR::LiteralExpr &expr,
944+
const TyTy::BaseType *tyty)
945+
{
946+
rust_assert (expr.get_lit_type () == HIR::Literal::FLOAT);
947+
const auto literal_value = expr.get_literal ();
948+
949+
mpfr_t fval;
950+
if (mpfr_init_set_str (fval, literal_value.as_string ().c_str (), 10,
951+
MPFR_RNDN)
952+
!= 0)
953+
{
954+
rust_error_at (expr.get_locus (), "bad number in literal");
955+
return error_mark_node;
956+
}
957+
958+
tree type = TyTyResolveCompile::compile (ctx, tyty);
959+
960+
// taken from:
961+
// see go/gofrontend/expressions.cc:check_float_type
962+
mpfr_exp_t exp = mpfr_get_exp (fval);
963+
bool real_value_overflow = exp > TYPE_PRECISION (type);
964+
965+
REAL_VALUE_TYPE r1;
966+
real_from_mpfr (&r1, fval, type, GMP_RNDN);
967+
REAL_VALUE_TYPE r2;
968+
real_convert (&r2, TYPE_MODE (type), &r1);
969+
970+
tree real_value = build_real (type, r2);
971+
if (TREE_OVERFLOW (real_value) || real_value_overflow)
972+
{
973+
rust_error_at (expr.get_locus (),
974+
"decimal overflows the respective type %<%s%>",
975+
tyty->get_name ().c_str ());
976+
return error_mark_node;
977+
}
978+
979+
return real_value;
980+
}
981+
982+
tree
983+
CompileExpr::compile_char_literal (const HIR::LiteralExpr &expr,
984+
const TyTy::BaseType *tyty)
985+
{
986+
rust_assert (expr.get_lit_type () == HIR::Literal::CHAR);
987+
const auto literal_value = expr.get_literal ();
988+
989+
// FIXME needs wchar_t
990+
char c = literal_value.as_string ().c_str ()[0];
991+
return ctx->get_backend ()->wchar_constant_expression (c);
992+
}
993+
994+
tree
995+
CompileExpr::compile_byte_literal (const HIR::LiteralExpr &expr,
996+
const TyTy::BaseType *tyty)
997+
{
998+
rust_assert (expr.get_lit_type () == HIR::Literal::BYTE);
999+
const auto literal_value = expr.get_literal ();
1000+
1001+
tree type = TyTyResolveCompile::compile (ctx, tyty);
1002+
char c = literal_value.as_string ().c_str ()[0];
1003+
return build_int_cst (type, c);
1004+
}
1005+
1006+
tree
1007+
CompileExpr::compile_string_literal (const HIR::LiteralExpr &expr,
1008+
const TyTy::BaseType *tyty)
1009+
{
1010+
rust_assert (expr.get_lit_type () == HIR::Literal::STRING);
1011+
const auto literal_value = expr.get_literal ();
1012+
1013+
auto base = ctx->get_backend ()->string_constant_expression (
1014+
literal_value.as_string ());
1015+
return ctx->get_backend ()->address_expression (base, expr.get_locus ());
1016+
}
1017+
1018+
tree
1019+
CompileExpr::compile_byte_string_literal (const HIR::LiteralExpr &expr,
1020+
const TyTy::BaseType *tyty)
1021+
{
1022+
rust_assert (expr.get_lit_type () == HIR::Literal::BYTE_STRING);
1023+
1024+
// the type here is &[ty; capacity]
1025+
rust_assert (tyty->get_kind () == TyTy::TypeKind::REF);
1026+
const auto ref_tyty = static_cast<const TyTy::ReferenceType *> (tyty);
1027+
auto base_tyty = ref_tyty->get_base ();
1028+
rust_assert (base_tyty->get_kind () == TyTy::TypeKind::ARRAY);
1029+
auto array_tyty = static_cast<TyTy::ArrayType *> (base_tyty);
1030+
1031+
std::string value_str = expr.get_literal ().as_string ();
1032+
std::vector<tree> vals;
1033+
std::vector<unsigned long> indexes;
1034+
for (size_t i = 0; i < value_str.size (); i++)
1035+
{
1036+
char b = value_str.at (i);
1037+
tree bb = ctx->get_backend ()->char_constant_expression (b);
1038+
vals.push_back (bb);
1039+
indexes.push_back (i);
1040+
}
1041+
1042+
tree array_type = TyTyResolveCompile::compile (ctx, array_tyty);
1043+
tree constructed
1044+
= ctx->get_backend ()->array_constructor_expression (array_type, indexes,
1045+
vals,
1046+
expr.get_locus ());
1047+
1048+
return ctx->get_backend ()->address_expression (constructed,
1049+
expr.get_locus ());
1050+
}
1051+
1052+
tree
1053+
CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
1054+
Location location)
1055+
{
1056+
if (type_to_cast_to == error_mark_node || expr_tree == error_mark_node
1057+
|| TREE_TYPE (expr_tree) == error_mark_node)
1058+
return error_mark_node;
1059+
1060+
if (ctx->get_backend ()->type_size (type_to_cast_to) == 0
1061+
|| TREE_TYPE (expr_tree) == void_type_node)
1062+
{
1063+
// Do not convert zero-sized types.
1064+
return expr_tree;
1065+
}
1066+
else if (TREE_CODE (type_to_cast_to) == INTEGER_TYPE)
1067+
{
1068+
tree cast = fold (convert_to_integer (type_to_cast_to, expr_tree));
1069+
// FIXME check for TREE_OVERFLOW?
1070+
return cast;
1071+
}
1072+
else if (TREE_CODE (type_to_cast_to) == REAL_TYPE)
1073+
{
1074+
tree cast = fold (convert_to_real (type_to_cast_to, expr_tree));
1075+
// FIXME
1076+
// We might need to check that the tree is MAX val and thusly saturate it
1077+
// to inf. we can get the bounds and check the value if its >= or <= to
1078+
// the min and max bounds
1079+
//
1080+
// https://github.com/Rust-GCC/gccrs/issues/635
1081+
return cast;
1082+
}
1083+
else if (TREE_CODE (type_to_cast_to) == COMPLEX_TYPE)
1084+
{
1085+
return fold (convert_to_complex (type_to_cast_to, expr_tree));
1086+
}
1087+
else if (TREE_CODE (type_to_cast_to) == POINTER_TYPE
1088+
&& TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE)
1089+
{
1090+
return fold (convert_to_pointer (type_to_cast_to, expr_tree));
1091+
}
1092+
else if (TREE_CODE (type_to_cast_to) == RECORD_TYPE
1093+
|| TREE_CODE (type_to_cast_to) == ARRAY_TYPE)
1094+
{
1095+
return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
1096+
type_to_cast_to, expr_tree);
1097+
}
1098+
1099+
return fold_convert_loc (location.gcc_location (), type_to_cast_to,
1100+
expr_tree);
1101+
}
1102+
8961103
} // namespace Compile
8971104
} // namespace Rust

0 commit comments

Comments
 (0)