Commit 5caf63ca by Ian Lance Taylor

compiler: Rewrite handling of untyped numeric constants.

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

From-SVN: r185925
parent b59e9071
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -18,6 +18,9 @@ class Traverse; ...@@ -18,6 +18,9 @@ class Traverse;
class Statement_inserter; class Statement_inserter;
class Type; class Type;
struct Type_context; struct Type_context;
class Integer_type;
class Float_type;
class Complex_type;
class Function_type; class Function_type;
class Map_type; class Map_type;
class Struct_type; class Struct_type;
...@@ -38,6 +41,7 @@ class Field_reference_expression; ...@@ -38,6 +41,7 @@ class Field_reference_expression;
class Interface_field_reference_expression; class Interface_field_reference_expression;
class Type_guard_expression; class Type_guard_expression;
class Receive_expression; class Receive_expression;
class Numeric_constant;
class Named_object; class Named_object;
class Export; class Export;
class Import; class Import;
...@@ -342,30 +346,11 @@ class Expression ...@@ -342,30 +346,11 @@ class Expression
is_constant() const is_constant() const
{ return this->do_is_constant(); } { return this->do_is_constant(); }
// If this is not a constant expression with integral type, return // If this is not a numeric constant, return false. If it is one,
// false. If it is one, return true, and set VAL to the value. VAL // return true, and set VAL to hold the value.
// 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.
bool bool
integer_constant_value(bool iota_is_constant, mpz_t val, 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 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;
// If this is not a constant expression with string type, return // If this is not a constant expression with string type, return
// false. If it is one, return true, and set VAL to the value. // false. If it is one, return true, and set VAL to the value.
...@@ -691,22 +676,10 @@ class Expression ...@@ -691,22 +676,10 @@ class Expression
do_is_constant() const do_is_constant() const
{ return false; } { return false; }
// Return whether this is a constant expression of integral type, // Return whether this is a constant expression of numeric type, and
// and set VAL to the value. // 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 virtual bool
do_float_constant_value(mpfr_t, Type**) const do_numeric_constant_value(Numeric_constant*) 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
{ return false; } { return false; }
// Return whether this is a constant expression of string type, and // Return whether this is a constant expression of string type, and
...@@ -1189,42 +1162,21 @@ class Binary_expression : public Expression ...@@ -1189,42 +1162,21 @@ class Binary_expression : public Expression
right() right()
{ return this->right_; } { return this->right_; }
// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL. // Apply binary opcode OP to LEFT_NC and RIGHT_NC, setting NC.
// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of // Return true if this could be done, false if not. Issue errors at
// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL. Return true // LOCATION as appropriate.
// if this could be done, false if not.
static bool static bool
eval_integer(Operator op, Type* left_type, mpz_t left_val, eval_constant(Operator op, Numeric_constant* left_nc,
Type* right_type, mpz_t right_val, Location, Numeric_constant* right_nc, Location location,
mpz_t val); Numeric_constant* nc);
// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL. // Compare constants LEFT_NC and RIGHT_NC according to OP, setting
// Return true if this could be done, false if not. // *RESULT. Return true if this could be done, false if not. Issue
// errors at LOCATION as appropriate.
static bool static bool
eval_float(Operator op, Type* left_type, mpfr_t left_val, compare_constant(Operator op, Numeric_constant* left_nc,
Type* right_type, mpfr_t right_val, mpfr_t val, Numeric_constant* right_nc, Location location,
Location); bool* result);
// 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);
static Expression* static Expression*
do_import(Import*); do_import(Import*);
...@@ -1246,13 +1198,7 @@ class Binary_expression : public Expression ...@@ -1246,13 +1198,7 @@ class Binary_expression : public Expression
{ return this->left_->is_constant() && this->right_->is_constant(); } { return this->left_->is_constant() && this->right_->is_constant(); }
bool bool
do_integer_constant_value(bool, mpz_t val, Type**) const; do_numeric_constant_value(Numeric_constant*) const;
bool
do_float_constant_value(mpfr_t val, Type**) const;
bool
do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
void void
do_discarding_value(); do_discarding_value();
...@@ -1283,6 +1229,34 @@ class Binary_expression : public Expression ...@@ -1283,6 +1229,34 @@ class Binary_expression : public Expression
do_dump_expression(Ast_dump_context*) const; do_dump_expression(Ast_dump_context*) const;
private: 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* Expression*
lower_struct_comparison(Gogo*, Statement_inserter*); lower_struct_comparison(Gogo*, Statement_inserter*);
...@@ -2101,4 +2075,173 @@ class Receive_expression : public Expression ...@@ -2101,4 +2075,173 @@ class Receive_expression : public Expression
Expression* channel_; 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) #endif // !defined(GO_EXPRESSIONS_H)
...@@ -3214,10 +3214,9 @@ class Case_clauses::Hash_integer_value ...@@ -3214,10 +3214,9 @@ class Case_clauses::Hash_integer_value
size_t size_t
Case_clauses::Hash_integer_value::operator()(Expression* pe) const Case_clauses::Hash_integer_value::operator()(Expression* pe) const
{ {
Type* itype; Numeric_constant nc;
mpz_t ival; mpz_t ival;
mpz_init(ival); if (!pe->numeric_constant_value(&nc) || !nc.to_int(&ival))
if (!pe->integer_constant_value(true, ival, &itype))
go_unreachable(); go_unreachable();
size_t ret = mpz_get_ui(ival); size_t ret = mpz_get_ui(ival);
mpz_clear(ival); mpz_clear(ival);
...@@ -3236,14 +3235,14 @@ class Case_clauses::Eq_integer_value ...@@ -3236,14 +3235,14 @@ class Case_clauses::Eq_integer_value
bool bool
Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const Case_clauses::Eq_integer_value::operator()(Expression* a, Expression* b) const
{ {
Type* atype; Numeric_constant anc;
Type* btype;
mpz_t aval; mpz_t aval;
Numeric_constant bnc;
mpz_t bval; mpz_t bval;
mpz_init(aval); if (!a->numeric_constant_value(&anc)
mpz_init(bval); || !anc.to_int(&aval)
if (!a->integer_constant_value(true, aval, &atype) || !b->numeric_constant_value(&bnc)
|| !b->integer_constant_value(true, bval, &btype)) || !bnc.to_int(&bval))
go_unreachable(); go_unreachable();
bool ret = mpz_cmp(aval, bval) == 0; bool ret = mpz_cmp(aval, bval) == 0;
mpz_clear(aval); mpz_clear(aval);
...@@ -3431,18 +3430,17 @@ Case_clauses::Case_clause::get_backend(Translate_context* context, ...@@ -3431,18 +3430,17 @@ Case_clauses::Case_clause::get_backend(Translate_context* context,
Expression* e = *p; Expression* e = *p;
if (e->classification() != Expression::EXPRESSION_INTEGER) if (e->classification() != Expression::EXPRESSION_INTEGER)
{ {
Type* itype; Numeric_constant nc;
mpz_t ival; mpz_t ival;
mpz_init(ival); if (!(*p)->numeric_constant_value(&nc) || !nc.to_int(&ival))
if (!(*p)->integer_constant_value(true, ival, &itype))
{ {
// Something went wrong. This can happen with a // Something went wrong. This can happen with a
// negative constant and an unsigned switch value. // negative constant and an unsigned switch value.
go_assert(saw_errors()); go_assert(saw_errors());
continue; continue;
} }
go_assert(itype != NULL); go_assert(nc.type() != NULL);
e = Expression::make_integer(&ival, itype, e->location()); e = Expression::make_integer(&ival, nc.type(), e->location());
mpz_clear(ival); mpz_clear(ival);
} }
......
...@@ -2230,15 +2230,13 @@ Type::is_backend_type_size_known(Gogo* gogo) ...@@ -2230,15 +2230,13 @@ Type::is_backend_type_size_known(Gogo* gogo)
return true; return true;
else else
{ {
Numeric_constant nc;
if (!at->length()->numeric_constant_value(&nc))
return false;
mpz_t ival; mpz_t ival;
mpz_init(ival); if (!nc.to_int(&ival))
Type* dummy;
bool length_known = at->length()->integer_constant_value(true,
ival,
&dummy);
mpz_clear(ival);
if (!length_known)
return false; return false;
mpz_clear(ival);
return at->element_type()->is_backend_type_size_known(gogo); 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 ...@@ -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 // Try to determine the lengths. If we can't, assume the arrays
// are not identical. // are not identical.
bool ret = false; bool ret = false;
mpz_t v1; Numeric_constant nc1, nc2;
mpz_init(v1); if (l1->numeric_constant_value(&nc1)
Type* type1; && l2->numeric_constant_value(&nc2))
mpz_t v2; {
mpz_init(v2); mpz_t v1;
Type* type2; if (nc1.to_int(&v1))
if (l1->integer_constant_value(true, v1, &type1) {
&& l2->integer_constant_value(true, v2, &type2)) mpz_t v2;
ret = mpz_cmp(v1, v2) == 0; if (nc2.to_int(&v2))
mpz_clear(v1); {
mpz_clear(v2); ret = mpz_cmp(v1, v2) == 0;
mpz_clear(v2);
}
mpz_clear(v1);
}
}
return ret; return ret;
} }
...@@ -5154,58 +5157,44 @@ Array_type::verify_length() ...@@ -5154,58 +5157,44 @@ Array_type::verify_length()
return false; return false;
} }
mpz_t val; Numeric_constant nc;
mpz_init(val); if (!this->length_->numeric_constant_value(&nc))
Type* vt;
if (!this->length_->integer_constant_value(true, val, &vt))
{ {
mpfr_t fval; if (this->length_->type()->integer_type() != NULL
mpfr_init(fval); || this->length_->type()->float_type() != NULL)
if (!this->length_->float_constant_value(fval, &vt)) error_at(this->length_->location(), "array bound is not constant");
{ else
if (this->length_->type()->integer_type() != NULL error_at(this->length_->location(), "array bound is not numeric");
|| this->length_->type()->float_type() != NULL) return false;
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 (mpz_sgn(val) < 0) unsigned long val;
switch (nc.to_unsigned_long(&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"); error_at(this->length_->location(), "negative array bound");
mpz_clear(val);
return false; 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"); Type* int_type = Type::lookup_integer_type("int");
int tbits = int_type->integer_type()->bits(); unsigned int tbits = int_type->integer_type()->bits();
int vbits = mpz_sizeinbase(val, 2); if (sizeof(val) <= tbits * 8
if (vbits + 1 > tbits) && val >> (tbits - 1) != 0)
{ {
error_at(this->length_->location(), "array bound overflows"); error_at(this->length_->location(), "array bound overflows");
mpz_clear(val);
return false; return false;
} }
mpz_clear(val);
return true; return true;
} }
...@@ -5457,11 +5446,11 @@ Array_type::get_length_tree(Gogo* gogo) ...@@ -5457,11 +5446,11 @@ Array_type::get_length_tree(Gogo* gogo)
go_assert(this->length_ != NULL); go_assert(this->length_ != NULL);
if (this->length_tree_ == NULL_TREE) if (this->length_tree_ == NULL_TREE)
{ {
Numeric_constant nc;
mpz_t val; mpz_t val;
mpz_init(val); if (this->length_->numeric_constant_value(&nc) && nc.to_int(&val))
Type* t;
if (this->length_->integer_constant_value(true, val, &t))
{ {
Type* t = nc.type();
if (t == NULL) if (t == NULL)
t = Type::lookup_integer_type("int"); t = Type::lookup_integer_type("int");
else if (t->is_abstract()) else if (t->is_abstract())
...@@ -5472,8 +5461,6 @@ Array_type::get_length_tree(Gogo* gogo) ...@@ -5472,8 +5461,6 @@ Array_type::get_length_tree(Gogo* gogo)
} }
else else
{ {
mpz_clear(val);
// Make up a translation context for the array length // Make up a translation context for the array length
// expression. FIXME: This won't work in general. // expression. FIXME: This won't work in general.
Translate_context context(gogo, NULL, NULL, NULL); Translate_context context(gogo, NULL, NULL, NULL);
...@@ -5824,23 +5811,17 @@ Array_type::do_reflection(Gogo* gogo, std::string* ret) const ...@@ -5824,23 +5811,17 @@ Array_type::do_reflection(Gogo* gogo, std::string* ret) const
ret->push_back('['); ret->push_back('[');
if (this->length_ != NULL) if (this->length_ != NULL)
{ {
mpz_t val; Numeric_constant nc;
mpz_init(val); unsigned long val;
Type* type; if (!this->length_->numeric_constant_value(&nc)
if (!this->length_->integer_constant_value(true, val, &type)) || nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID)
error_at(this->length_->location(), error_at(this->length_->location(), "invalid array length");
"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");
else else
{ {
char buf[50]; char buf[50];
snprintf(buf, sizeof buf, "%lu", mpz_get_ui(val)); snprintf(buf, sizeof buf, "%lu", val);
ret->append(buf); ret->append(buf);
} }
mpz_clear(val);
} }
ret->push_back(']'); ret->push_back(']');
...@@ -5856,23 +5837,17 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const ...@@ -5856,23 +5837,17 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const
this->append_mangled_name(this->element_type_, gogo, ret); this->append_mangled_name(this->element_type_, gogo, ret);
if (this->length_ != NULL) if (this->length_ != NULL)
{ {
mpz_t val; Numeric_constant nc;
mpz_init(val); unsigned long val;
Type* type; if (!this->length_->numeric_constant_value(&nc)
if (!this->length_->integer_constant_value(true, val, &type)) || nc.to_unsigned_long(&val) != Numeric_constant::NC_UL_VALID)
error_at(this->length_->location(), error_at(this->length_->location(), "invalid array length");
"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");
else else
{ {
char buf[50]; char buf[50];
snprintf(buf, sizeof buf, "%lu", mpz_get_ui(val)); snprintf(buf, sizeof buf, "%lu", val);
ret->append(buf); ret->append(buf);
} }
mpz_clear(val);
} }
ret->push_back('e'); ret->push_back('e');
} }
......
...@@ -680,6 +680,14 @@ class Type ...@@ -680,6 +680,14 @@ class Type
complex_type() const complex_type() const
{ return this->convert<const Complex_type, TYPE_COMPLEX>(); } { 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. // Return true if this is a boolean type.
bool bool
is_boolean_type() const is_boolean_type() const
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment