compiler: Rewrite handling of untyped numeric constants.

Fixes various bugs when, e.g., using float or complex
constants in integer contexts.

From-SVN: r185926
This commit is contained in:
Ian Lance Taylor 2012-03-28 21:27:48 +00:00
parent 23a24bba32
commit 1089b513c0
5 changed files with 1692 additions and 2079 deletions

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,9 @@ class Traverse;
class Statement_inserter;
class Type;
struct Type_context;
class Integer_type;
class Float_type;
class Complex_type;
class Function_type;
class Map_type;
class Struct_type;
@ -38,6 +41,7 @@ class Field_reference_expression;
class Interface_field_reference_expression;
class Type_guard_expression;
class Receive_expression;
class Numeric_constant;
class Named_object;
class Export;
class Import;
@ -342,30 +346,11 @@ class Expression
is_constant() const
{ return this->do_is_constant(); }
// If this is not a constant expression with integral type, return
// false. If it is one, return true, and set VAL to the value. VAL
// should already be initialized. If this returns true, it sets
// *PTYPE to the type of the value, or NULL for an abstract type.
// If IOTA_IS_CONSTANT is true, then an iota expression is assumed
// to have its final value.
// If this is not a numeric constant, return false. If it is one,
// return true, and set VAL to hold the value.
bool
integer_constant_value(bool iota_is_constant, mpz_t val, Type** ptype) const;
// If this is not a constant expression with floating point type,
// return false. If it is one, return true, and set VAL to the
// value. VAL should already be initialized. If this returns true,
// it sets *PTYPE to the type of the value, or NULL for an abstract
// type.
bool
float_constant_value(mpfr_t val, Type** ptype) const;
// If this is not a constant expression with complex type, return
// false. If it is one, return true, and set REAL and IMAG to the
// value. REAL and IMAG should already be initialized. If this
// return strue, it sets *PTYPE to the type of the value, or NULL
// for an abstract type.
bool
complex_constant_value(mpfr_t real, mpfr_t imag, Type** ptype) const;
numeric_constant_value(Numeric_constant* val) const
{ return this->do_numeric_constant_value(val); }
// If this is not a constant expression with string type, return
// false. If it is one, return true, and set VAL to the value.
@ -691,22 +676,10 @@ class Expression
do_is_constant() const
{ return false; }
// Return whether this is a constant expression of integral type,
// and set VAL to the value.
// Return whether this is a constant expression of numeric type, and
// set the Numeric_constant to the value.
virtual bool
do_integer_constant_value(bool, mpz_t, Type**) const
{ return false; }
// Return whether this is a constant expression of floating point
// type, and set VAL to the value.
virtual bool
do_float_constant_value(mpfr_t, Type**) const
{ return false; }
// Return whether this is a constant expression of complex type, and
// set REAL and IMAGE to the value.
virtual bool
do_complex_constant_value(mpfr_t, mpfr_t, Type**) const
do_numeric_constant_value(Numeric_constant*) const
{ return false; }
// Return whether this is a constant expression of string type, and
@ -1189,42 +1162,21 @@ class Binary_expression : public Expression
right()
{ return this->right_; }
// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL. Return true
// if this could be done, false if not.
// Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC.
// Return true if this could be done, false if not. Issue errors at
// LOCATION as appropriate.
static bool
eval_integer(Operator op, Type* left_type, mpz_t left_val,
Type* right_type, mpz_t right_val, Location,
mpz_t val);
eval_constant(Operator op, Numeric_constant* left_nc,
Numeric_constant* right_nc, Location location,
Numeric_constant* nc);
// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
// Return true if this could be done, false if not.
// Compare constants LEFT_NC and RIGHT_NC according to OP, setting
// *RESULT. Return true if this could be done, false if not. Issue
// errors at LOCATION as appropriate.
static bool
eval_float(Operator op, Type* left_type, mpfr_t left_val,
Type* right_type, mpfr_t right_val, mpfr_t val,
Location);
// Apply binary opcode OP to LEFT_REAL/LEFT_IMAG and
// RIGHT_REAL/RIGHT_IMAG, setting REAL/IMAG. Return true if this
// could be done, false if not.
static bool
eval_complex(Operator op, Type* left_type, mpfr_t left_real,
mpfr_t left_imag, Type* right_type, mpfr_t right_real,
mpfr_t right_imag, mpfr_t real, mpfr_t imag, Location);
// Compare integer constants according to OP.
static bool
compare_integer(Operator op, mpz_t left_val, mpz_t right_val);
// Compare floating point constants according to OP.
static bool
compare_float(Operator op, Type* type, mpfr_t left_val, mpfr_t right_val);
// Compare complex constants according to OP.
static bool
compare_complex(Operator op, Type* type, mpfr_t left_real, mpfr_t left_imag,
mpfr_t right_val, mpfr_t right_imag);
compare_constant(Operator op, Numeric_constant* left_nc,
Numeric_constant* right_nc, Location location,
bool* result);
static Expression*
do_import(Import*);
@ -1246,13 +1198,7 @@ class Binary_expression : public Expression
{ return this->left_->is_constant() && this->right_->is_constant(); }
bool
do_integer_constant_value(bool, mpz_t val, Type**) const;
bool
do_float_constant_value(mpfr_t val, Type**) const;
bool
do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
do_numeric_constant_value(Numeric_constant*) const;
void
do_discarding_value();
@ -1283,6 +1229,34 @@ class Binary_expression : public Expression
do_dump_expression(Ast_dump_context*) const;
private:
static bool
operation_type(Operator op, Type* left_type, Type* right_type,
Type** result_type);
static bool
cmp_to_bool(Operator op, int cmp);
static bool
eval_integer(Operator op, const Numeric_constant*, const Numeric_constant*,
Location, Numeric_constant*);
static bool
eval_float(Operator op, const Numeric_constant*, const Numeric_constant*,
Location, Numeric_constant*);
static bool
eval_complex(Operator op, const Numeric_constant*, const Numeric_constant*,
Location, Numeric_constant*);
static bool
compare_integer(const Numeric_constant*, const Numeric_constant*, int*);
static bool
compare_float(const Numeric_constant*, const Numeric_constant *, int*);
static bool
compare_complex(const Numeric_constant*, const Numeric_constant*, int*);
Expression*
lower_struct_comparison(Gogo*, Statement_inserter*);
@ -2101,4 +2075,173 @@ class Receive_expression : public Expression
Expression* channel_;
};
// A numeric constant. This is used both for untyped constants and
// for constants that have a type.
class Numeric_constant
{
public:
Numeric_constant()
: classification_(NC_INVALID), type_(NULL)
{ }
~Numeric_constant();
Numeric_constant(const Numeric_constant&);
Numeric_constant& operator=(const Numeric_constant&);
// Set to an unsigned long value.
void
set_unsigned_long(Type*, unsigned long);
// Set to an integer value.
void
set_int(Type*, const mpz_t);
// Set to a rune value.
void
set_rune(Type*, const mpz_t);
// Set to a floating point value.
void
set_float(Type*, const mpfr_t);
// Set to a complex value.
void
set_complex(Type*, const mpfr_t, const mpfr_t);
// Classifiers.
bool
is_int() const
{ return this->classification_ == Numeric_constant::NC_INT; }
bool
is_rune() const
{ return this->classification_ == Numeric_constant::NC_RUNE; }
bool
is_float() const
{ return this->classification_ == Numeric_constant::NC_FLOAT; }
bool
is_complex() const
{ return this->classification_ == Numeric_constant::NC_COMPLEX; }
// Value retrievers. These will initialize the values as well as
// set them. GET_INT is only valid if IS_INT returns true, and
// likewise respectively.
void
get_int(mpz_t*) const;
void
get_rune(mpz_t*) const;
void
get_float(mpfr_t*) const;
void
get_complex(mpfr_t*, mpfr_t*) const;
// Codes returned by to_unsigned_long.
enum To_unsigned_long
{
// Value is integer and fits in unsigned long.
NC_UL_VALID,
// Value is not integer.
NC_UL_NOTINT,
// Value is integer but is negative.
NC_UL_NEGATIVE,
// Value is non-negative integer but does not fit in unsigned
// long.
NC_UL_BIG
};
// If the value can be expressed as an integer that fits in an
// unsigned long, set *VAL and return NC_UL_VALID. Otherwise return
// one of the other To_unsigned_long codes.
To_unsigned_long
to_unsigned_long(unsigned long* val) const;
// If the value can be expressed as an int, return true and
// initialize and set VAL. This will return false for a value with
// an explicit float or complex type, even if the value is integral.
bool
to_int(mpz_t* val) const;
// If the value can be expressed as a float, return true and
// initialize and set VAL.
bool
to_float(mpfr_t* val) const;
// If the value can be expressed as a complex, return true and
// initialize and set VR and VI.
bool
to_complex(mpfr_t* vr, mpfr_t* vi) const;
// Get the type.
Type*
type() const;
// 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, issue an error message. LOCATION is
// the location to use for the error.
bool
set_type(Type* type, bool issue_error, Location location);
// Return an Expression for this value.
Expression*
expression(Location) const;
private:
void
clear();
To_unsigned_long
mpz_to_unsigned_long(const mpz_t ival, unsigned long *val) const;
To_unsigned_long
mpfr_to_unsigned_long(const mpfr_t fval, unsigned long *val) const;
bool
check_int_type(Integer_type*, bool, Location) const;
bool
check_float_type(Float_type*, bool, Location) const;
bool
check_complex_type(Complex_type*, bool, Location) const;
// The kinds of constants.
enum Classification
{
NC_INVALID,
NC_RUNE,
NC_INT,
NC_FLOAT,
NC_COMPLEX
};
// The kind of constant.
Classification classification_;
// The value.
union
{
// If NC_INT or NC_RUNE.
mpz_t int_val;
// If NC_FLOAT.
mpfr_t float_val;
// If NC_COMPLEX.
struct
{
mpfr_t real;
mpfr_t imag;
} complex_val;
} u_;
// The type if there is one. This will be NULL for an untyped
// constant.
Type* type_;
};
#endif // !defined(GO_EXPRESSIONS_H)

View File

@ -3214,10 +3214,9 @@ class Case_clauses::Hash_integer_value
size_t
Case_clauses::Hash_integer_value::operator()(Expression* pe) const
{
Type* itype;
Numeric_constant nc;
mpz_t ival;
mpz_init(ival);
if (!pe->integer_constant_value(true, ival, &itype))
if (!pe->numeric_constant_value(&nc) || !nc.to_int(&ival))
go_unreachable();
size_t ret = mpz_get_ui(ival);
mpz_clear(ival);
@ -3236,14 +3235,14 @@ class Case_clauses::Eq_integer_value
bool
Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const
{
Type* atype;
Type* btype;
Numeric_constant anc;
mpz_t aval;
Numeric_constant bnc;
mpz_t bval;
mpz_init(aval);
mpz_init(bval);
if (!a->integer_constant_value(true, aval, &atype)
|| !b->integer_constant_value(true, bval, &btype))
if (!a->numeric_constant_value(&anc)
|| !anc.to_int(&aval)
|| !b->numeric_constant_value(&bnc)
|| !bnc.to_int(&bval))
go_unreachable();
bool ret = mpz_cmp(aval, bval) == 0;
mpz_clear(aval);
@ -3431,18 +3430,17 @@ Case_clauses::Case_clause::get_backend(Translate_context* context,
Expression* e = *p;
if (e->classification() != Expression::EXPRESSION_INTEGER)
{
Type* itype;
Numeric_constant nc;
mpz_t ival;
mpz_init(ival);
if (!(*p)->integer_constant_value(true, ival, &itype))
if (!(*p)->numeric_constant_value(&nc) || !nc.to_int(&ival))
{
// Something went wrong. This can happen with a
// negative constant and an unsigned switch value.
go_assert(saw_errors());
continue;
}
go_assert(itype != NULL);
e = Expression::make_integer(&ival, itype, e->location());
go_assert(nc.type() != NULL);
e = Expression::make_integer(&ival, nc.type(), e->location());
mpz_clear(ival);
}

View File

@ -2230,15 +2230,13 @@ Type::is_backend_type_size_known(Gogo* gogo)
return true;
else
{
mpz_t ival;
mpz_init(ival);
Type* dummy;
bool length_known = at->length()->integer_constant_value(true,
ival,
&dummy);
mpz_clear(ival);
if (!length_known)
Numeric_constant nc;
if (!at->length()->numeric_constant_value(&nc))
return false;
mpz_t ival;
if (!nc.to_int(&ival))
return false;
mpz_clear(ival);
return at->element_type()->is_backend_type_size_known(gogo);
}
}
@ -5106,17 +5104,22 @@ Array_type::is_identical(const Array_type* t, bool errors_are_identical) const
// Try to determine the lengths. If we can't, assume the arrays
// are not identical.
bool ret = false;
mpz_t v1;
mpz_init(v1);
Type* type1;
mpz_t v2;
mpz_init(v2);
Type* type2;
if (l1->integer_constant_value(true, v1, &type1)
&& l2->integer_constant_value(true, v2, &type2))
ret = mpz_cmp(v1, v2) == 0;
mpz_clear(v1);
mpz_clear(v2);
Numeric_constant nc1, nc2;
if (l1->numeric_constant_value(&nc1)
&& l2->numeric_constant_value(&nc2))
{
mpz_t v1;
if (nc1.to_int(&v1))
{
mpz_t v2;
if (nc2.to_int(&v2))
{
ret = mpz_cmp(v1, v2) == 0;
mpz_clear(v2);
}
mpz_clear(v1);
}
}
return ret;
}
@ -5154,58 +5157,44 @@ Array_type::verify_length()
return false;
}
mpz_t val;
mpz_init(val);
Type* vt;
if (!this->length_->integer_constant_value(true, val, &vt))
Numeric_constant nc;
if (!this->length_->numeric_constant_value(&nc))
{
mpfr_t fval;
mpfr_init(fval);
if (!this->length_->float_constant_value(fval, &vt))
{
if (this->length_->type()->integer_type() != NULL
|| this->length_->type()->float_type() != NULL)
error_at(this->length_->location(),
"array bound is not constant");
else
error_at(this->length_->location(),
"array bound is not numeric");
mpfr_clear(fval);
mpz_clear(val);
return false;
}
if (!mpfr_integer_p(fval))
{
error_at(this->length_->location(),
"array bound truncated to integer");
mpfr_clear(fval);
mpz_clear(val);
return false;
}
mpz_init(val);
mpfr_get_z(val, fval, GMP_RNDN);
mpfr_clear(fval);
if (this->length_->type()->integer_type() != NULL
|| this->length_->type()->float_type() != NULL)
error_at(this->length_->location(), "array bound is not constant");
else
error_at(this->length_->location(), "array bound is not numeric");
return false;
}
if (mpz_sgn(val) < 0)
unsigned long val;
switch (nc.to_unsigned_long(&val))
{
error_at(this->length_->location(), "negative array bound");
mpz_clear(val);
case Numeric_constant::NC_UL_VALID:
break;
case Numeric_constant::NC_UL_NOTINT:
error_at(this->length_->location(), "array bound truncated to integer");
return false;
case Numeric_constant::NC_UL_NEGATIVE:
error_at(this->length_->location(), "negative array bound");
return false;
case Numeric_constant::NC_UL_BIG:
error_at(this->length_->location(), "array bound overflows");
return false;
default:
go_unreachable();
}
Type* int_type = Type::lookup_integer_type("int");
int tbits = int_type->integer_type()->bits();
int vbits = mpz_sizeinbase(val, 2);
if (vbits + 1 > tbits)
unsigned int tbits = int_type->integer_type()->bits();
if (sizeof(val) <= tbits * 8
&& val >> (tbits - 1) != 0)
{
error_at(this->length_->location(), "array bound overflows");
mpz_clear(val);
return false;
}
mpz_clear(val);
return true;
}
@ -5457,11 +5446,11 @@ Array_type::get_length_tree(Gogo* gogo)
go_assert(this->length_ != NULL);
if (this->length_tree_ == NULL_TREE)
{
Numeric_constant nc;
mpz_t val;
mpz_init(val);
Type* t;
if (this->length_->integer_constant_value(true, val, &t))
if (this->length_->numeric_constant_value(&nc) && nc.to_int(&val))
{
Type* t = nc.type();
if (t == NULL)
t = Type::lookup_integer_type("int");
else if (t->is_abstract())
@ -5472,8 +5461,6 @@ Array_type::get_length_tree(Gogo* gogo)
}
else
{
mpz_clear(val);
// Make up a translation context for the array length
// expression. FIXME: This won't work in general.
Translate_context context(gogo, NULL, NULL, NULL);
@ -5824,23 +5811,17 @@ Array_type::do_reflection(Gogo* gogo, std::string* ret) const
ret->push_back('[');
if (this->length_ != NULL)
{
mpz_t val;
mpz_init(val);
Type* type;
if (!this->length_->integer_constant_value(true, val, &type))
error_at(this->length_->location(),
"array length must be integer constant expression");
else if (mpz_cmp_si(val, 0) < 0)
error_at(this->length_->location(), "array length is negative");
else if (mpz_cmp_ui(val, mpz_get_ui(val)) != 0)
error_at(this->length_->location(), "array length is too large");
Numeric_constant nc;
unsigned long val;
if (!this->length_->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID)
error_at(this->length_->location(), "invalid array length");
else
{
char buf[50];
snprintf(buf, sizeof buf, "%lu", mpz_get_ui(val));
snprintf(buf, sizeof buf, "%lu", val);
ret->append(buf);
}
mpz_clear(val);
}
ret->push_back(']');
@ -5856,23 +5837,17 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const
this->append_mangled_name(this->element_type_, gogo, ret);
if (this->length_ != NULL)
{
mpz_t val;
mpz_init(val);
Type* type;
if (!this->length_->integer_constant_value(true, val, &type))
error_at(this->length_->location(),
"array length must be integer constant expression");
else if (mpz_cmp_si(val, 0) < 0)
error_at(this->length_->location(), "array length is negative");
else if (mpz_cmp_ui(val, mpz_get_ui(val)) != 0)
error_at(this->length_->location(), "array size is too large");
Numeric_constant nc;
unsigned long val;
if (!this->length_->numeric_constant_value(&nc)
|| nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID)
error_at(this->length_->location(), "invalid array length");
else
{
char buf[50];
snprintf(buf, sizeof buf, "%lu", mpz_get_ui(val));
snprintf(buf, sizeof buf, "%lu", val);
ret->append(buf);
}
mpz_clear(val);
}
ret->push_back('e');
}

View File

@ -680,6 +680,14 @@ class Type
complex_type() const
{ return this->convert<const Complex_type, TYPE_COMPLEX>(); }
// Return whether this is a numeric type.
bool
is_numeric_type() const
{
Type_classification tc = this->base()->classification_;
return tc == TYPE_INTEGER || tc == TYPE_FLOAT || tc == TYPE_COMPLEX;
}
// Return true if this is a boolean type.
bool
is_boolean_type() const