+
+// Class Numeric_constant.
+
+// Destructor.
+
+Numeric_constant::~Numeric_constant()
+{
+ this->clear();
+}
+
+// Copy constructor.
+
+Numeric_constant::Numeric_constant(const Numeric_constant& a)
+ : classification_(a.classification_), type_(a.type_)
+{
+ switch (a.classification_)
+ {
+ case NC_INVALID:
+ break;
+ case NC_INT:
+ case NC_RUNE:
+ mpz_init_set(this->u_.int_val, a.u_.int_val);
+ break;
+ case NC_FLOAT:
+ mpfr_init_set(this->u_.float_val, a.u_.float_val, GMP_RNDN);
+ break;
+ case NC_COMPLEX:
+ mpfr_init_set(this->u_.complex_val.real, a.u_.complex_val.real,
+ GMP_RNDN);
+ mpfr_init_set(this->u_.complex_val.imag, a.u_.complex_val.imag,
+ GMP_RNDN);
+ break;
+ default:
+ go_unreachable();
+ }
+}
+
+// Assignment operator.
+
+Numeric_constant&
+Numeric_constant::operator=(const Numeric_constant& a)
+{
+ this->clear();
+ this->classification_ = a.classification_;
+ this->type_ = a.type_;
+ switch (a.classification_)
+ {
+ case NC_INVALID:
+ break;
+ case NC_INT:
+ case NC_RUNE:
+ mpz_init_set(this->u_.int_val, a.u_.int_val);
+ break;
+ case NC_FLOAT:
+ mpfr_init_set(this->u_.float_val, a.u_.float_val, GMP_RNDN);
+ break;
+ case NC_COMPLEX:
+ mpfr_init_set(this->u_.complex_val.real, a.u_.complex_val.real,
+ GMP_RNDN);
+ mpfr_init_set(this->u_.complex_val.imag, a.u_.complex_val.imag,
+ GMP_RNDN);
+ break;
+ default:
+ go_unreachable();
+ }
+ return *this;
+}
+
+// Clear the contents.
+
+void
+Numeric_constant::clear()
+{
+ switch (this->classification_)
+ {
+ case NC_INVALID:
+ break;
+ case NC_INT:
+ case NC_RUNE:
+ mpz_clear(this->u_.int_val);
+ break;
+ case NC_FLOAT:
+ mpfr_clear(this->u_.float_val);
+ break;
+ case NC_COMPLEX:
+ mpfr_clear(this->u_.complex_val.real);
+ mpfr_clear(this->u_.complex_val.imag);
+ break;
+ default:
+ go_unreachable();
+ }
+ this->classification_ = NC_INVALID;
+}
+
+// Set to an unsigned long value.
+
+void
+Numeric_constant::set_unsigned_long(Type* type, unsigned long val)
+{
+ this->clear();
+ this->classification_ = NC_INT;
+ this->type_ = type;
+ mpz_init_set_ui(this->u_.int_val, val);
+}
+
+// Set to an integer value.
+
+void
+Numeric_constant::set_int(Type* type, const mpz_t val)
+{
+ this->clear();
+ this->classification_ = NC_INT;
+ this->type_ = type;
+ mpz_init_set(this->u_.int_val, val);
+}
+
+// Set to a rune value.
+
+void
+Numeric_constant::set_rune(Type* type, const mpz_t val)
+{
+ this->clear();
+ this->classification_ = NC_RUNE;
+ this->type_ = type;
+ mpz_init_set(this->u_.int_val, val);
+}
+
+// Set to a floating point value.
+
+void
+Numeric_constant::set_float(Type* type, const mpfr_t val)
+{
+ this->clear();
+ this->classification_ = NC_FLOAT;
+ this->type_ = type;
+ // Numeric constants do not have negative zero values, so remove
+ // them here. They also don't have infinity or NaN values, but we
+ // should never see them here.
+ if (mpfr_zero_p(val))
+ mpfr_init_set_ui(this->u_.float_val, 0, GMP_RNDN);
+ else
+ mpfr_init_set(this->u_.float_val, val, GMP_RNDN);
+}
+
+// Set to a complex value.
+
+void
+Numeric_constant::set_complex(Type* type, const mpfr_t real, const mpfr_t imag)
+{
+ this->clear();
+ this->classification_ = NC_COMPLEX;
+ this->type_ = type;
+ mpfr_init_set(this->u_.complex_val.real, real, GMP_RNDN);
+ mpfr_init_set(this->u_.complex_val.imag, imag, GMP_RNDN);
+}
+
+// Get an int value.
+
+void
+Numeric_constant::get_int(mpz_t* val) const
+{
+ go_assert(this->is_int());
+ mpz_init_set(*val, this->u_.int_val);
+}
+
+// Get a rune value.
+
+void
+Numeric_constant::get_rune(mpz_t* val) const
+{
+ go_assert(this->is_rune());
+ mpz_init_set(*val, this->u_.int_val);
+}
+
+// Get a floating point value.
+
+void
+Numeric_constant::get_float(mpfr_t* val) const
+{
+ go_assert(this->is_float());
+ mpfr_init_set(*val, this->u_.float_val, GMP_RNDN);
+}
+
+// Get a complex value.
+
+void
+Numeric_constant::get_complex(mpfr_t* real, mpfr_t* imag) const
+{
+ go_assert(this->is_complex());
+ mpfr_init_set(*real, this->u_.complex_val.real, GMP_RNDN);
+ mpfr_init_set(*imag, this->u_.complex_val.imag, GMP_RNDN);
+}
+
+// Express value as unsigned long if possible.
+
+Numeric_constant::To_unsigned_long
+Numeric_constant::to_unsigned_long(unsigned long* val) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ return this->mpz_to_unsigned_long(this->u_.int_val, val);
+ case NC_FLOAT:
+ return this->mpfr_to_unsigned_long(this->u_.float_val, val);
+ case NC_COMPLEX:
+ if (!mpfr_zero_p(this->u_.complex_val.imag))
+ return NC_UL_NOTINT;
+ return this->mpfr_to_unsigned_long(this->u_.complex_val.real, val);
+ default:
+ go_unreachable();
+ }
+}
+
+// Express integer value as unsigned long if possible.
+
+Numeric_constant::To_unsigned_long
+Numeric_constant::mpz_to_unsigned_long(const mpz_t ival,
+ unsigned long *val) const
+{
+ if (mpz_sgn(ival) < 0)
+ return NC_UL_NEGATIVE;
+ unsigned long ui = mpz_get_ui(ival);
+ if (mpz_cmp_ui(ival, ui) != 0)
+ return NC_UL_BIG;
+ *val = ui;
+ return NC_UL_VALID;
+}
+
+// Express floating point value as unsigned long if possible.
+
+Numeric_constant::To_unsigned_long
+Numeric_constant::mpfr_to_unsigned_long(const mpfr_t fval,
+ unsigned long *val) const
+{
+ if (!mpfr_integer_p(fval))
+ return NC_UL_NOTINT;
+ mpz_t ival;
+ mpz_init(ival);
+ mpfr_get_z(ival, fval, GMP_RNDN);
+ To_unsigned_long ret = this->mpz_to_unsigned_long(ival, val);
+ mpz_clear(ival);
+ return ret;
+}
+
+// Convert value to integer if possible.
+
+bool
+Numeric_constant::to_int(mpz_t* val) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpz_init_set(*val, this->u_.int_val);
+ return true;
+ case NC_FLOAT:
+ if (!mpfr_integer_p(this->u_.float_val))
+ return false;
+ mpz_init(*val);
+ mpfr_get_z(*val, this->u_.float_val, GMP_RNDN);
+ return true;
+ case NC_COMPLEX:
+ if (!mpfr_zero_p(this->u_.complex_val.imag)
+ || !mpfr_integer_p(this->u_.complex_val.real))
+ return false;
+ mpz_init(*val);
+ mpfr_get_z(*val, this->u_.complex_val.real, GMP_RNDN);
+ return true;
+ default:
+ go_unreachable();
+ }
+}
+
+// Convert value to floating point if possible.
+
+bool
+Numeric_constant::to_float(mpfr_t* val) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpfr_init_set_z(*val, this->u_.int_val, GMP_RNDN);
+ return true;
+ case NC_FLOAT:
+ mpfr_init_set(*val, this->u_.float_val, GMP_RNDN);
+ return true;
+ case NC_COMPLEX:
+ if (!mpfr_zero_p(this->u_.complex_val.imag))
+ return false;
+ mpfr_init_set(*val, this->u_.complex_val.real, GMP_RNDN);
+ return true;
+ default:
+ go_unreachable();
+ }
+}
+
+// Convert value to complex.
+
+bool
+Numeric_constant::to_complex(mpfr_t* vr, mpfr_t* vi) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpfr_init_set_z(*vr, this->u_.int_val, GMP_RNDN);
+ mpfr_init_set_ui(*vi, 0, GMP_RNDN);
+ return true;
+ case NC_FLOAT:
+ mpfr_init_set(*vr, this->u_.float_val, GMP_RNDN);
+ mpfr_init_set_ui(*vi, 0, GMP_RNDN);
+ return true;
+ case NC_COMPLEX:
+ mpfr_init_set(*vr, this->u_.complex_val.real, GMP_RNDN);
+ mpfr_init_set(*vi, this->u_.complex_val.imag, GMP_RNDN);
+ return true;
+ default:
+ go_unreachable();
+ }
+}
+
+// Get the type.
+
+Type*
+Numeric_constant::type() const
+{
+ if (this->type_ != NULL)
+ return this->type_;
+ switch (this->classification_)
+ {
+ case NC_INT:
+ return Type::make_abstract_integer_type();
+ case NC_RUNE:
+ return Type::make_abstract_character_type();
+ case NC_FLOAT:
+ return Type::make_abstract_float_type();
+ case NC_COMPLEX:
+ return Type::make_abstract_complex_type();
+ default:
+ go_unreachable();
+ }
+}
+
+// If the constant can be expressed in TYPE, then set the type of the
+// constant to TYPE and return true. Otherwise return false, and, if
+// ISSUE_ERROR is true, report an appropriate error message.
+
+bool
+Numeric_constant::set_type(Type* type, bool issue_error, Location loc)
+{
+ bool ret;
+ if (type == NULL)
+ ret = true;
+ else if (type->integer_type() != NULL)
+ ret = this->check_int_type(type->integer_type(), issue_error, loc);
+ else if (type->float_type() != NULL)
+ ret = this->check_float_type(type->float_type(), issue_error, loc);
+ else if (type->complex_type() != NULL)
+ ret = this->check_complex_type(type->complex_type(), issue_error, loc);
+ else
+ go_unreachable();
+ if (ret)
+ this->type_ = type;
+ return ret;
+}
+
+// Check whether the constant can be expressed in an integer type.
+
+bool
+Numeric_constant::check_int_type(Integer_type* type, bool issue_error,
+ Location location) const
+{
+ mpz_t val;
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpz_init_set(val, this->u_.int_val);
+ break;
+
+ case NC_FLOAT:
+ if (!mpfr_integer_p(this->u_.float_val))
+ {
+ if (issue_error)
+ error_at(location, "floating point constant truncated to integer");
+ return false;
+ }
+ mpz_init(val);
+ mpfr_get_z(val, this->u_.float_val, GMP_RNDN);
+ break;
+
+ case NC_COMPLEX:
+ if (!mpfr_integer_p(this->u_.complex_val.real)
+ || !mpfr_zero_p(this->u_.complex_val.imag))
+ {
+ if (issue_error)
+ error_at(location, "complex constant truncated to integer");
+ return false;
+ }
+ mpz_init(val);
+ mpfr_get_z(val, this->u_.complex_val.real, GMP_RNDN);
+ break;
+
+ default:
+ go_unreachable();
+ }
+
+ bool ret;
+ if (type->is_abstract())
+ ret = true;
+ else
+ {
+ int bits = mpz_sizeinbase(val, 2);
+ if (type->is_unsigned())
+ {
+ // For an unsigned type we can only accept a nonnegative
+ // number, and we must be able to represents at least BITS.
+ ret = mpz_sgn(val) >= 0 && bits <= type->bits();
+ }
+ else
+ {
+ // For a signed type we need an extra bit to indicate the
+ // sign. We have to handle the most negative integer
+ // specially.
+ ret = (bits + 1 <= type->bits()
+ || (bits <= type->bits()
+ && mpz_sgn(val) < 0
+ && (mpz_scan1(val, 0)
+ == static_cast<unsigned long>(type->bits() - 1))
+ && mpz_scan0(val, type->bits()) == ULONG_MAX));
+ }
+ }
+
+ if (!ret && issue_error)
+ error_at(location, "integer constant overflow");
+
+ return ret;
+}
+
+// Check whether the constant can be expressed in a floating point
+// type.
+
+bool
+Numeric_constant::check_float_type(Float_type* type, bool issue_error,
+ Location location) const
+{
+ mpfr_t val;
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpfr_init_set_z(val, this->u_.int_val, GMP_RNDN);
+ break;
+
+ case NC_FLOAT:
+ mpfr_init_set(val, this->u_.float_val, GMP_RNDN);
+ break;
+
+ case NC_COMPLEX:
+ if (!mpfr_zero_p(this->u_.complex_val.imag))
+ {
+ if (issue_error)
+ error_at(location, "complex constant truncated to float");
+ return false;
+ }
+ mpfr_init_set(val, this->u_.complex_val.real, GMP_RNDN);
+ break;
+
+ default:
+ go_unreachable();
+ }
+
+ bool ret;
+ if (type->is_abstract())
+ ret = true;
+ else if (mpfr_nan_p(val) || mpfr_inf_p(val) || mpfr_zero_p(val))
+ {
+ // A NaN or Infinity always fits in the range of the type.
+ ret = true;
+ }
+ else
+ {
+ mp_exp_t exp = mpfr_get_exp(val);
+ mp_exp_t max_exp;
+ switch (type->bits())
+ {
+ case 32:
+ max_exp = 128;
+ break;
+ case 64:
+ max_exp = 1024;
+ break;
+ default:
+ go_unreachable();
+ }
+
+ ret = exp <= max_exp;
+ }
+
+ mpfr_clear(val);
+
+ if (!ret && issue_error)
+ error_at(location, "floating point constant overflow");
+
+ return ret;
+}
+
+// Check whether the constant can be expressed in a complex type.
+
+bool
+Numeric_constant::check_complex_type(Complex_type* type, bool issue_error,
+ Location location) const
+{
+ if (type->is_abstract())
+ return true;
+
+ mp_exp_t max_exp;
+ switch (type->bits())
+ {
+ case 64:
+ max_exp = 128;
+ break;
+ case 128:
+ max_exp = 1024;
+ break;
+ default:
+ go_unreachable();
+ }
+
+ mpfr_t real;
+ switch (this->classification_)
+ {
+ case NC_INT:
+ case NC_RUNE:
+ mpfr_init_set_z(real, this->u_.int_val, GMP_RNDN);
+ break;
+
+ case NC_FLOAT:
+ mpfr_init_set(real, this->u_.float_val, GMP_RNDN);
+ break;
+
+ case NC_COMPLEX:
+ if (!mpfr_nan_p(this->u_.complex_val.imag)
+ && !mpfr_inf_p(this->u_.complex_val.imag)
+ && !mpfr_zero_p(this->u_.complex_val.imag))
+ {
+ if (mpfr_get_exp(this->u_.complex_val.imag) > max_exp)
+ {
+ if (issue_error)
+ error_at(location, "complex imaginary part overflow");
+ return false;
+ }
+ }
+ mpfr_init_set(real, this->u_.complex_val.real, GMP_RNDN);
+ break;
+
+ default:
+ go_unreachable();
+ }
+
+ bool ret;
+ if (mpfr_nan_p(real) || mpfr_inf_p(real) || mpfr_zero_p(real))
+ ret = true;
+ else
+ ret = mpfr_get_exp(real) <= max_exp;
+
+ mpfr_clear(real);
+
+ if (!ret && issue_error)
+ error_at(location, "complex real part overflow");
+
+ return ret;
+}
+
+// Return an Expression for this value.
+
+Expression*
+Numeric_constant::expression(Location loc) const
+{
+ switch (this->classification_)
+ {
+ case NC_INT:
+ return Expression::make_integer(&this->u_.int_val, this->type_, loc);
+ case NC_RUNE:
+ return Expression::make_character(&this->u_.int_val, this->type_, loc);
+ case NC_FLOAT:
+ return Expression::make_float(&this->u_.float_val, this->type_, loc);
+ case NC_COMPLEX:
+ return Expression::make_complex(&this->u_.complex_val.real,
+ &this->u_.complex_val.imag,
+ this->type_, loc);
+ default:
+ go_unreachable();
+ }
+}