This patch from Chris Manghane fixes the Go frontend to correctly report unused packages when there are ambiguous lookups. Previously some cases involving composite literals could cause a package to be considered as used, when it was really not used. That is, in some cases, the compiler did not report an error that it should have reported. This is http://golang.org/issue/6427 .
Bootstrapped and ran testsuite on x86_64-unknown-linux-gnu. Committed to mainline. Ian
Index: gcc/go/gofrontend/expressions.cc =================================================================== --- gcc/go/gofrontend/expressions.cc (revision 217402) +++ gcc/go/gofrontend/expressions.cc (working copy) @@ -12791,6 +12791,16 @@ Composite_literal_expression::lower_stru { case EXPRESSION_UNKNOWN_REFERENCE: name = name_expr->unknown_expression()->name(); + if (type->named_type() != NULL) + { + // If the named object found for this field name comes from a + // different package than the struct it is a part of, do not count + // this incorrect lookup as a usage of the object's package. + no = name_expr->unknown_expression()->named_object(); + if (no->package() != NULL + && no->package() != type->named_type()->named_object()->package()) + no->package()->forget_usage(name_expr); + } break; case EXPRESSION_CONST_REFERENCE: Index: gcc/go/gofrontend/gogo.cc =================================================================== --- gcc/go/gofrontend/gogo.cc (revision 217402) +++ gcc/go/gofrontend/gogo.cc (working copy) @@ -1412,7 +1412,7 @@ Gogo::lookup(const std::string& name, Na if (ret != NULL) { if (ret->package() != NULL) - ret->package()->set_used(); + ret->package()->note_usage(); return ret; } } @@ -7426,6 +7426,36 @@ Package::set_priority(int priority) this->priority_ = priority; } +// Forget a given usage. If forgetting this usage means this package becomes +// unused, report that error. + +void +Package::forget_usage(Expression* usage) const +{ + if (this->fake_uses_.empty()) + return; + + std::set<Expression*>::iterator p = this->fake_uses_.find(usage); + go_assert(p != this->fake_uses_.end()); + this->fake_uses_.erase(p); + + if (this->fake_uses_.empty()) + error_at(this->location(), "imported and not used: %s", + Gogo::message_name(this->package_name()).c_str()); +} + +// Clear the used field for the next file. If the only usages of this package +// are possibly fake, keep the fake usages for lowering. + +void +Package::clear_used() +{ + if (this->used_ > this->fake_uses_.size()) + this->fake_uses_.clear(); + + this->used_ = 0; +} + // Determine types of constants. Everything else in a package // (variables, function declarations) should already have a fixed // type. Constants may have abstract types. Index: gcc/go/gofrontend/gogo.h =================================================================== --- gcc/go/gofrontend/gogo.h (revision 217402) +++ gcc/go/gofrontend/gogo.h (working copy) @@ -2645,17 +2645,25 @@ class Package // Whether some symbol from the package was used. bool used() const - { return this->used_; } + { return this->used_ > 0; } // Note that some symbol from this package was used. void - set_used() const - { this->used_ = true; } + note_usage() const + { this->used_++; } + + // Note that USAGE might be a fake usage of this package. + void + note_fake_usage(Expression* usage) const + { this->fake_uses_.insert(usage); } + + // Forget a given USAGE of this package. + void + forget_usage(Expression* usage) const; // Clear the used field for the next file. void - clear_used() - { this->used_ = false; } + clear_used(); // Whether this package was imported in the current file. bool @@ -2749,10 +2757,12 @@ class Package int priority_; // The location of the import statement. Location location_; - // True if some name from this package was used. This is mutable - // because we can use a package even if we have a const pointer to - // it. - mutable bool used_; + // The amount of times some name from this package was used. This is mutable + // because we can use a package even if we have a const pointer to it. + mutable size_t used_; + // A set of possibly fake uses of this package. This is mutable because we + // can track fake uses of a package even if we have a const pointer to it. + mutable std::set<Expression*> fake_uses_; // True if this package was imported in the current file. bool is_imported_; // True if this package was imported with a name of "_". Index: gcc/go/gofrontend/parse.cc =================================================================== --- gcc/go/gofrontend/parse.cc (revision 217402) +++ gcc/go/gofrontend/parse.cc (working copy) @@ -199,7 +199,7 @@ Parse::qualified_ident(std::string* pnam return false; } - package->package_value()->set_used(); + package->package_value()->note_usage(); token = this->advance_token(); if (!token->is_identifier()) @@ -2401,7 +2401,7 @@ Parse::operand(bool may_be_sink, bool* i return Expression::make_error(location); } package = named_object->package_value(); - package->set_used(); + package->note_usage(); id = this->peek_token()->identifier(); is_exported = this->peek_token()->is_identifier_exported(); packed = this->gogo_->pack_hidden_name(id, is_exported); @@ -3242,9 +3242,12 @@ Parse::id_to_expression(const std::strin case Named_object::NAMED_OBJECT_TYPE_DECLARATION: { // These cases can arise for a field name in a composite - // literal. + // literal. Keep track of these as they might be fake uses of + // the related package. Unknown_expression* ue = Expression::make_unknown_reference(named_object, location); + if (named_object->package() != NULL) + named_object->package()->note_fake_usage(ue); if (this->is_erroneous_function_) ue->set_no_error_message(); return ue;