This Go frontend patch by Ben Shi checks for duplicate numeric keys in map literals. This is for https://golang.org/issue/28104. Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed to mainline.
Ian
Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 269241) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -2c74b84184941ebea318f69fe43a81f657790b63 +bc036b3a03e089e78b892067e40dbb0e7ecca9e2 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 269196) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -14454,6 +14454,7 @@ Composite_literal_expression::lower_map( { Location location = this->location(); Unordered_map(unsigned int, std::vector<Expression*>) st; + Unordered_map(unsigned int, std::vector<Expression*>) nt; if (this->vals_ != NULL) { if (!this->has_keys_) @@ -14488,8 +14489,8 @@ Composite_literal_expression::lower_map( if (!(*p)->is_constant()) continue; std::string sval; - // Check if there are duplicate constant string keys. - if ((*p)->string_constant_value(&sval)) + Numeric_constant nval; + if ((*p)->string_constant_value(&sval)) // Check string keys. { unsigned int h = Gogo::hash_string(sval, 0); // Search the index h in the hash map. @@ -14526,6 +14527,42 @@ Composite_literal_expression::lower_map( mit->second.push_back(*p); } } + else if ((*p)->numeric_constant_value(&nval)) // Check numeric keys. + { + unsigned int h = nval.hash(0); + Unordered_map(unsigned int, std::vector<Expression*>)::iterator mit; + mit = nt.find(h); + if (mit == nt.end()) + { + // No duplicate since h is a new code. + // Create a new vector indexed by h and add it to the hash map. + std::vector<Expression*> l; + l.push_back(*p); + std::pair<unsigned int, std::vector<Expression*> > val(h, l); + nt.insert(val); + } + else + { + // Do further check since h already exists. + for (std::vector<Expression*>::iterator lit = + mit->second.begin(); + lit != mit->second.end(); + lit++) + { + Numeric_constant rval; + bool ok = (*lit)->numeric_constant_value(&rval); + go_assert(ok); + if (nval.equals(rval)) + { + go_error_at((*p)->location(), + "duplicate key in map literal"); + return Expression::make_error(location); + } + } + // Add this new numeric key to the vector indexed by h. + mit->second.push_back(*p); + } + } } } @@ -16472,6 +16509,36 @@ Numeric_constant::operator=(const Numeri return *this; } +// Check equality with another numeric constant. + +bool +Numeric_constant::equals(const Numeric_constant& a) const +{ + if (this->classification_ != a.classification_) + return false; + + if (this->type_ != NULL && a.type_ != NULL + && !Type::are_identical(this->type_, a.type_, + Type::COMPARE_ALIASES, NULL)) + return false; + + switch (a.classification_) + { + case NC_INVALID: + break; + case NC_INT: + case NC_RUNE: + return mpz_cmp(this->u_.int_val, a.u_.int_val) == 0; + case NC_FLOAT: + return mpfr_cmp(this->u_.float_val, a.u_.float_val) == 0; + case NC_COMPLEX: + return mpc_cmp(this->u_.complex_val, a.u_.complex_val) == 0; + default: + go_unreachable(); + } + return false; +} + // Clear the contents. void @@ -17198,3 +17265,40 @@ Numeric_constant::expression(Location lo go_unreachable(); } } + +// Calculate a hash code with a given seed. + +unsigned int +Numeric_constant::hash(unsigned int seed) const +{ + unsigned long val; + const unsigned int PRIME = 97; + long e = 0; + double f = 1.0; + mpfr_t m; + + switch (this->classification_) + { + case NC_INVALID: + return PRIME; + case NC_INT: + case NC_RUNE: + val = mpz_get_ui(this->u_.int_val); + break; + case NC_COMPLEX: + mpfr_init(m); + mpc_abs(m, this->u_.complex_val, MPFR_RNDN); + val = mpfr_get_ui(m, MPFR_RNDN); + mpfr_clear(m); + break; + case NC_FLOAT: + f = mpfr_get_d_2exp(&e, this->u_.float_val, MPFR_RNDN) * 4294967295.0; + val = static_cast<unsigned long>(e + static_cast<long>(f)); + break; + default: + go_unreachable(); + } + + return (static_cast<unsigned int>(val) + seed) * PRIME; +} + Index: gcc/go/gofrontend/expressions.h =================================================================== --- gcc/go/gofrontend/expressions.h (revision 269196) +++ gcc/go/gofrontend/expressions.h (working copy) @@ -4163,6 +4163,10 @@ class Numeric_constant Numeric_constant& operator=(const Numeric_constant&); + // Check equality with another numeric constant. + bool + equals(const Numeric_constant&) const; + // Set to an unsigned long value. void set_unsigned_long(Type*, unsigned long); @@ -4282,6 +4286,10 @@ class Numeric_constant Expression* expression(Location) const; + // Calculate a hash code with a given seed. + unsigned int + hash(unsigned int seed) const; + private: void clear();