// Perform the usual arithmetic conversions on `e1` and `e2`. This // tries to find a common type for `e1` and `e2` and convert both // expressions to that type. // // NOTE: In C++ lvalue-to-rvalue conversions are required on a // per-expression basis, independently of converting to a common // type. Also, non-user-defined types are unqualified prior to // analysis. It would be easier if we found an absolute common // type and then instantiated a declaration suitable for overload // resolution. Maybe. // // TODO: Handle conversions for character types (or promote to a // corresponding integer type?). // // TODO: How does bool work with this set of conversions? Promote // bool to int? // // TODO: Can we unify this with the common type required by the // conditional expression? Note that the arithmetic version converts // to values, and the conditional expression can retain references. Expr_pair arithmetic_conversions(Context& cxt, Expr& e1, Expr& e2) { // If the types are the same, no conversions are applied. if (is_equivalent(e1.type(), e2.type())) return {e1, e2}; // If either operand has floating point type, convert to the type // with the greatest precision. // // TODO: Why isn't convert_to_common_float symmetric? This seems like // an issue. if (has_floating_point_type(e1)) return convert_to_common_float(cxt, e2, e1); if (has_floating_point_type(e2)) return convert_to_common_float(cxt, e1, e2); // If both operands have integer type, the following rules apply. if (has_integer_type(e1) && has_integer_type(e2)) return convert_to_common_int(cxt, e1, e2); // TODO: No conversion from e1 to e2. error(cxt, "no usual arithmetic conversion of '{}' and '{}'", e1, e2); throw Type_error(); }
// Assuming the type of e1 is untested and e2 has floating point // type, convert to the most precise floating point type. Expr_pair convert_to_common_float(Context& cxt, Expr& e1, Expr& e2) { Float_type& f2 = cast<Float_type>(e2.type()); // If e1 has float type, convert to the most precise type. if (has_floating_point_type(e1)) { Float_type& f1 = cast<Float_type>(e1.type()); if (f1.precision() < f2.precision()) { Expr& c = convert_to_wider_float(cxt, e1, f2); return {c, e2}; } if (f2.precision() < f1.precision()) { Expr& c = convert_to_wider_float(cxt, e2, f1); return {e1, c}; } lingo_unreachable(); } // If e1 has integer type, convert to e2. if (has_integer_type(e1)) { Expr& c = convert_integer_to_float(cxt, e1, f2); return {c, e2}; } error(cxt, "no floating point conversions for '{}' and '{}'", e1, e2); throw Type_error(); }
// Perform the usual arithmetic conversions on `e1` and `e2`. This // tries to find a common type for `e1` and `e2` and convert both // expressions to that type. // // NOTE: In C++ lvalue-to-rvalue conversions are required on a // per-expression basis, independently of converting to a common // type. Also, non-user-defined types are unqualified prior to // analysis. It would be easier if we found an absolute common // type and then instantiated a declaration suitable for overload // resolution. Maybe. // // TODO: Handle conversions for character types (or promote to a // corresponding integer type?). // // TODO: How does bool work with this set of conversions? Promote // bool to int? // // TODO: Can we unify this with the common type required by the // conditional expression? Note that the arithmetic version converts // to values, and the conditional expression can retain references. Expr_pair arithmetic_conversion(Expr& e1, Expr& e2) { // If the types are the same, no conversions are applied. if (is_equivalent(e1.type(), e2.type())) return {e1, e2}; // If either operand has floating point type, convert to the type // with the greatest precision. if (has_floating_point_type(e1)) return convert_to_common_float(e2, e1); if (has_floating_point_type(e2)) return convert_to_common_float(e1, e2); // If both oerands have integer type, the following rules apply. if (has_integer_type(e1) && has_integer_type(e2)) return convert_to_common_int(e1, e2); // TODO: No conversion from e1 to e2. throw std::runtime_error("incompatible types"); }
// Assuming the type of e1 is untested and e2 has floating point // type, convert to the most precise floating point type. Expr_pair convert_to_common_float(Expr& e1, Expr& e2) { Float_type& f2 = cast<Float_type>(e2.type()); // If e1 has float type, convert to the most precise. if (has_floating_point_type(e1)) { Float_type& f1 = cast<Float_type>(e1.type()); if (f1.precision() < f2.precision()) return {convert_to_wider_float(e1, f2), e2}; if (f2.precision() < f1.precision()) return {e1, convert_to_wider_float(e2, f1)}; } // If e1 has integer type, convert to e2. if (has_integer_type(e1)) { return {convert_integer_to_float(e1, f2), e2}; } throw std::runtime_error("incompatible types"); }
// A value of integer type can be converted to a value of a // wider integer type. // // A value of type bool can be converted to integer s.t. // false is 0 and true is 1. // // FIXME: An int-to-int conversion requires some form of sign // extension. That depends on the destination type. Perhaps use // different conversions for these values? // // Also use a different conversion for bool-to-int? Expr& convert_to_wider_integer(Expr& e, Integer_type& t) { // A value of integer type can be converted... if (has_integer_type(e)) { Integer_type& et = cast<Integer_type>(e.type()); // TODO: Be more precise about the conversion that's // actually going to happen. Especially, if we convert // sign and widen simultaneously. if (et.precision() < t.precision()) return *new Integer_conv(t, e); else if (et.sign() != t.sign()) return *new Integer_conv(t, e); else return e; } // A value of type bool can be converted... if (is<Boolean_type>(&e.type())) return *new Integer_conv(t, e); return e; }