From 476242fa5ceaa91393562814471c385ebfdd41f3 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 19 Jul 2021 11:53:05 -0700 Subject: compiler: avoid aliases in receiver types If a package declares a method on an alias type, the alias would be used in the export data. This would then trigger a compiler assertion on import: we should not be adding methods to aliases. Fix the problem by ensuring that receiver types do not use alias types. This seems preferable to consistently avoiding aliases in export data, as aliases can cross packages. And it's painful to try to patch this while writing the export data, as at that point all the types are known. Test case is https://golang.org/cl/335172. Fixes golang/go#47131 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/335729 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/gogo.cc | 40 ++++++++++++++++++++++++++++++++++++ gcc/go/gofrontend/gogo.h | 8 ++++++++ gcc/go/gofrontend/types.cc | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 4d0f44f..5323e18 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -adcf10890833026437a94da54934ce50c0018309 +920549b6382a2623538d31001271941f0e9e5a51 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 2872d2e..95b76bd 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -5763,6 +5763,26 @@ Function::check_labels() const } } +// Set the receiver type. This is used to remove aliases. + +void +Function::set_receiver_type(Type* rtype) +{ + Function_type* oft = this->type_; + Typed_identifier* rec = new Typed_identifier(oft->receiver()->name(), + rtype, + oft->receiver()->location()); + Typed_identifier_list* parameters = NULL; + if (oft->parameters() != NULL) + parameters = oft->parameters()->copy(); + Typed_identifier_list* results = NULL; + if (oft->results() != NULL) + results = oft->results()->copy(); + Function_type* nft = Type::make_function_type(rec, parameters, results, + oft->location()); + this->type_ = nft; +} + // Swap one function with another. This is used when building the // thunk we use to call a function which calls recover. It may not // work for any other case. @@ -7285,6 +7305,26 @@ Function_declaration::set_nointerface() this->pragmas_ |= GOPRAGMA_NOINTERFACE; } +// Set the receiver type. This is used to remove aliases. + +void +Function_declaration::set_receiver_type(Type* rtype) +{ + Function_type* oft = this->fntype_; + Typed_identifier* rec = new Typed_identifier(oft->receiver()->name(), + rtype, + oft->receiver()->location()); + Typed_identifier_list* parameters = NULL; + if (oft->parameters() != NULL) + parameters = oft->parameters()->copy(); + Typed_identifier_list* results = NULL; + if (oft->results() != NULL) + results = oft->results()->copy(); + Function_type* nft = Type::make_function_type(rec, parameters, results, + oft->location()); + this->fntype_ = nft; +} + // Import an inlinable function. This is used for an inlinable // function whose body is recorded in the export data. Parse the // export data into a Block and create a regular function using that diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index f4155a2..c49bc92 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -1724,6 +1724,10 @@ class Function set_is_referenced_by_inline() { this->is_referenced_by_inline_ = true; } + // Set the receiver type. This is used to remove aliases. + void + set_receiver_type(Type* rtype); + // Swap with another function. Used only for the thunk which calls // recover. void @@ -1990,6 +1994,10 @@ class Function_declaration set_is_on_inlinable_list() { this->is_on_inlinable_list_ = true; } + // Set the receiver type. This is used to remove aliases. + void + set_receiver_type(Type* rtype); + // Import the function body, creating a function. void import_function_body(Gogo*, Named_object*); diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index d08cbc9..ab7166b 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -10416,6 +10416,57 @@ Named_type::finalize_methods(Gogo* gogo) return; } + // Remove any aliases in the local method receiver types. + Bindings* methods = this->local_methods_; + if (methods != NULL) + { + for (Bindings::const_declarations_iterator p = + methods->begin_declarations(); + p != methods->end_declarations(); + ++p) + { + Named_object* no = p->second; + Function_type* fntype; + if (no->is_function()) + fntype = no->func_value()->type(); + else if (no->is_function_declaration()) + fntype = no->func_declaration_value()->type(); + else + { + go_assert(saw_errors()); + continue; + } + + Type* rtype = fntype->receiver()->type(); + bool is_pointer = false; + Type* pt = rtype->points_to(); + if (pt != NULL) + { + rtype = pt; + is_pointer = true; + } + if (rtype->named_type() != this) + { + if (rtype->unalias() != this) + { + go_assert(saw_errors()); + continue; + } + + rtype = this; + if (is_pointer) + rtype = Type::make_pointer_type(rtype); + + if (no->is_function()) + no->func_value()->set_receiver_type(rtype); + else if (no->is_function_declaration()) + no->func_declaration_value()->set_receiver_type(rtype); + else + go_unreachable(); + } + } + } + Type::finalize_methods(gogo, this, this->location_, &this->all_methods_); } -- cgit v1.1 From 06d0437d4a5faca2b695918cbe1d54a61935c98b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 31 Jul 2021 19:28:51 -0700 Subject: compiler, runtime: support unsafe.Add and unsafe.Slice For golang/go#19367 For golang/go#40481 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338949 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 227 +++++++++++++++++++++++++++++++++++++++ gcc/go/gofrontend/expressions.h | 4 +- gcc/go/gofrontend/runtime.def | 6 ++ gcc/go/gofrontend/unsafe.cc | 16 +++ 5 files changed, 253 insertions(+), 2 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 5323e18..19fbd64 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -920549b6382a2623538d31001271941f0e9e5a51 +ad667e7c70cea9fa5730660d72ad891b5753eb62 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index a0472ac..46e71e5 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -8252,12 +8252,16 @@ Builtin_call_expression::Builtin_call_expression(Gogo* gogo, this->code_ = BUILTIN_REAL; else if (name == "recover") this->code_ = BUILTIN_RECOVER; + else if (name == "Add") + this->code_ = BUILTIN_ADD; else if (name == "Alignof") this->code_ = BUILTIN_ALIGNOF; else if (name == "Offsetof") this->code_ = BUILTIN_OFFSETOF; else if (name == "Sizeof") this->code_ = BUILTIN_SIZEOF; + else if (name == "Slice") + this->code_ = BUILTIN_SLICE; else go_unreachable(); } @@ -8694,6 +8698,119 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, return Runtime::make_call(code, loc, 3, e1, e2, e3); } + + case BUILTIN_ADD: + { + Expression* ptr = this->args()->front(); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + ptr = Expression::make_cast(uintptr_type, ptr, loc); + Expression* len = this->args()->back(); + len = Expression::make_cast(uintptr_type, len, loc); + Expression* add = Expression::make_binary(OPERATOR_PLUS, ptr, len, + loc); + return Expression::make_cast(this->args()->front()->type(), add, loc); + } + + case BUILTIN_SLICE: + { + Expression* ptr = this->args()->front(); + Temporary_statement* ptr_temp = NULL; + if (!ptr->is_multi_eval_safe()) + { + ptr_temp = Statement::make_temporary(NULL, ptr, loc); + inserter->insert(ptr_temp); + ptr = Expression::make_temporary_reference(ptr_temp, loc); + } + + Expression* len = this->args()->back(); + Temporary_statement* len_temp = NULL; + if (!len->is_multi_eval_safe()) + { + len_temp = Statement::make_temporary(NULL, len, loc); + inserter->insert(len_temp); + len = Expression::make_temporary_reference(len_temp, loc); + } + + bool fits_in_int; + Numeric_constant nc; + if (this->args()->back()->numeric_constant_value(&nc)) + { + // We gave an error for constants that don't fit in int in + // check_types. + fits_in_int = true; + } + else + { + Integer_type* itype = this->args()->back()->type()->integer_type(); + go_assert(itype != NULL); + int ebits = itype->bits(); + int intbits = + Type::lookup_integer_type("int")->integer_type()->bits(); + + // We can treat ebits == intbits as small even for an + // unsigned integer type, because we will convert the + // value to int and then reject it in the runtime if it is + // negative. + + fits_in_int = ebits <= intbits; + } + + Runtime::Function code = (fits_in_int + ? Runtime::UNSAFESLICE + : Runtime::UNSAFESLICE64); + Expression* td = + Expression::make_type_descriptor(ptr->type()->points_to(), loc); + Expression* check = Runtime::make_call(code, loc, 3, + td, ptr, len); + + if (ptr_temp == NULL) + ptr = ptr->copy(); + else + ptr = Expression::make_temporary_reference(ptr_temp, loc); + Expression* nil = Expression::make_nil(loc); + nil = Expression::make_cast(ptr->type(), nil, loc); + Expression* is_nil = Expression::make_binary(OPERATOR_EQEQ, ptr, nil, + loc); + + if (len_temp == NULL) + len = len->copy(); + else + len = Expression::make_temporary_reference(len_temp, loc); + Expression* zero = Expression::make_integer_ul(0, len->type(), loc); + Expression* is_zero = Expression::make_binary(OPERATOR_EQEQ, len, zero, + loc); + + Expression* cond = Expression::make_binary(OPERATOR_ANDAND, is_nil, + is_zero, loc); + + Type* slice_type = Type::make_array_type(ptr->type()->points_to(), + NULL); + nil = Expression::make_nil(loc); + Expression* nil_slice = Expression::make_cast(slice_type, nil, loc); + + if (ptr_temp == NULL) + ptr = ptr->copy(); + else + ptr = Expression::make_temporary_reference(ptr_temp, loc); + + if (len_temp == NULL) + len = len->copy(); + else + len = Expression::make_temporary_reference(len_temp, loc); + + Expression* cap; + if (len_temp == NULL) + cap = len->copy(); + else + cap = Expression::make_temporary_reference(len_temp, loc); + + Expression* slice = Expression::make_slice_value(slice_type, ptr, + len, cap, loc); + + slice = Expression::make_conditional(cond, nil_slice, slice, loc); + + return Expression::make_compound(check, slice, loc); + } } return this; @@ -9781,9 +9898,11 @@ Builtin_call_expression::do_discarding_value() case BUILTIN_MAKE: case BUILTIN_NEW: case BUILTIN_REAL: + case BUILTIN_ADD: case BUILTIN_ALIGNOF: case BUILTIN_OFFSETOF: case BUILTIN_SIZEOF: + case BUILTIN_SLICE: this->unused_value_error(); return false; @@ -9890,6 +10009,18 @@ Builtin_call_expression::do_type() t = Type::make_error_type(); return t; } + + case BUILTIN_ADD: + return Type::make_pointer_type(Type::make_void_type()); + + case BUILTIN_SLICE: + const Expression_list* args = this->args(); + if (args == NULL || args->size() != 2) + return Type::make_error_type(); + Type* pt = args->front()->type()->points_to(); + if (pt == NULL) + return Type::make_error_type(); + return Type::make_array_type(pt, NULL); } } @@ -9954,6 +10085,28 @@ Builtin_call_expression::do_determine_type(const Type_context* context) is_print = false; break; + case BUILTIN_ADD: + case BUILTIN_SLICE: + // Both unsafe.Add and unsafe.Slice take two arguments, and the + // second arguments defaults to "int". + if (args != NULL && args->size() == 2) + { + if (this->code_ == BUILTIN_SLICE) + args->front()->determine_type_no_context(); + else + { + Type* pointer = Type::make_pointer_type(Type::make_void_type()); + Type_context subcontext(pointer, false); + args->front()->determine_type(&subcontext); + } + Type* int_type = Type::lookup_integer_type("int"); + Type_context subcontext(int_type, false); + args->back()->determine_type(&subcontext); + return; + } + is_print = false; + break; + default: is_print = false; break; @@ -10353,6 +10506,78 @@ Builtin_call_expression::do_check_types(Gogo*) } break; + case BUILTIN_ADD: + case BUILTIN_SLICE: + { + Numeric_constant nc; + unsigned long v; + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 2) + this->report_error(_("not enough arguments")); + else if (args->size() > 2) + this->report_error(_("too many arguments")); + else if (args->front()->is_error_expression() + || args->front()->type()->is_error() + || args->back()->is_error_expression() + || args->back()->type()->is_error()) + this->set_is_error(); + else if (args->back()->type()->integer_type() == NULL + && (!args->back()->type()->is_abstract() + || !args->back()->numeric_constant_value(&nc) + || (nc.to_unsigned_long(&v) + == Numeric_constant::NC_UL_NOTINT))) + { + if (this->code_ == BUILTIN_ADD) + go_error_at(args->back()->location(), "non-integer offset"); + else + go_error_at(args->back()->location(), "non-integer size"); + } + else if (this->code_ == BUILTIN_ADD) + { + Type* pointer_type = + Type::make_pointer_type(Type::make_void_type()); + std::string reason; + if (!Type::are_assignable(pointer_type, args->front()->type(), + &reason)) + { + if (reason.empty()) + go_error_at(args->front()->location(), + "argument 1 has incompatible type"); + else + go_error_at(args->front()->location(), + "argument 1 has incompatible type (%s)", + reason.c_str()); + this->set_is_error(); + } + } + else + { + if (args->front()->type()->points_to() == NULL) + { + go_error_at(args->front()->location(), + "argument 1 must be a pointer"); + this->set_is_error(); + } + + unsigned int int_bits = + Type::lookup_integer_type("int")->integer_type()->bits(); + + mpz_t ival; + if (args->back()->numeric_constant_value(&nc) && nc.to_int(&ival)) + { + if (mpz_sgn(ival) < 0 + || mpz_sizeinbase(ival, 2) >= int_bits) + { + go_error_at(args->back()->location(), + "slice length out of range"); + this->set_is_error(); + } + mpz_clear(ival); + } + } + } + break; + default: go_unreachable(); } @@ -10397,6 +10622,8 @@ Builtin_call_expression::do_get_backend(Translate_context* context) case BUILTIN_INVALID: case BUILTIN_NEW: case BUILTIN_MAKE: + case BUILTIN_ADD: + case BUILTIN_SLICE: go_unreachable(); case BUILTIN_LEN: diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 57c974d..492849b 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -2608,9 +2608,11 @@ class Builtin_call_expression : public Call_expression BUILTIN_RECOVER, // Builtin functions from the unsafe package. + BUILTIN_ADD, BUILTIN_ALIGNOF, BUILTIN_OFFSETOF, - BUILTIN_SIZEOF + BUILTIN_SIZEOF, + BUILTIN_SLICE }; Builtin_function_code diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index ec01be0..231d92d 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -489,6 +489,12 @@ DEF_GO_RUNTIME(ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", P3(POINTER, UINT8, INT32), R1(UINT8)) +// Check the length of an unsafe slice. +DEF_GO_RUNTIME(UNSAFESLICE, "runtime.unsafeslice", + P3(TYPE, POINTER, INT), R0()) +DEF_GO_RUNTIME(UNSAFESLICE64, "runtime.unsafeslice64", + P3(TYPE, POINTER, INT64), R0()) + // Panic reporting a division by zero. DEF_GO_RUNTIME(PANIC_DIVIDE, "runtime.panicdivide", P0(), R0()) diff --git a/gcc/go/gofrontend/unsafe.cc b/gcc/go/gofrontend/unsafe.cc index 9ed5b9d..18bd99e 100644 --- a/gcc/go/gofrontend/unsafe.cc +++ b/gcc/go/gofrontend/unsafe.cc @@ -86,6 +86,22 @@ Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported, if (add_to_globals) this->add_dot_import_object(no); + // Add. + results = new Typed_identifier_list; + results->push_back(Typed_identifier("", pointer_type, bloc)); + fntype = Type::make_function_type(NULL, NULL, results, bloc); + fntype->set_is_builtin(); + no = bindings->add_function_declaration("Add", package, fntype, bloc); + if (add_to_globals) + this->add_dot_import_object(no); + + // Slice. + fntype = Type::make_function_type(NULL, NULL, NULL, bloc); + fntype->set_is_builtin(); + no = bindings->add_function_declaration("Slice", package, fntype, bloc); + if (add_to_globals) + this->add_dot_import_object(no); + if (!this->imported_unsafe_) { go_imported_unsafe(); -- cgit v1.1 From 7459bfa8a37a4fbd6ed5153bff76f49d372b4ace Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 30 Jul 2021 17:19:42 -0700 Subject: compiler, runtime: allow slice to array pointer conversion Panic if the slice is too short. For golang/go#395 Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/338630 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 53 ++++++++++++++++++++++++++++++++++++++-- gcc/go/gofrontend/runtime.def | 5 ++++ gcc/go/gofrontend/types.cc | 7 ++++++ 4 files changed, 64 insertions(+), 3 deletions(-) (limited to 'gcc/go') diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 19fbd64..95b9340 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -ad667e7c70cea9fa5730660d72ad891b5753eb62 +0a4d612e6b211780b294717503fc739bbd1f509c The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 46e71e5..15c9eab 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3866,11 +3866,12 @@ Type_conversion_expression::do_traverse(Traverse* traverse) return TRAVERSE_CONTINUE; } -// Convert to a constant at lowering time. +// Convert to a constant at lowering time. Also lower conversions +// from slice to pointer-to-array, as they can panic. Expression* Type_conversion_expression::do_lower(Gogo*, Named_object*, - Statement_inserter*, int) + Statement_inserter* inserter, int) { Type* type = this->type_; Expression* val = this->expr_; @@ -3958,6 +3959,54 @@ Type_conversion_expression::do_lower(Gogo*, Named_object*, } } + if (type->points_to() != NULL + && type->points_to()->array_type() != NULL + && !type->points_to()->is_slice_type() + && val->type()->is_slice_type()) + { + Temporary_statement* val_temp = NULL; + if (!val->is_multi_eval_safe()) + { + val_temp = Statement::make_temporary(val->type(), NULL, location); + inserter->insert(val_temp); + val = Expression::make_set_and_use_temporary(val_temp, val, + location); + } + + Type* int_type = Type::lookup_integer_type("int"); + Temporary_statement* vallen_temp = + Statement::make_temporary(int_type, NULL, location); + inserter->insert(vallen_temp); + + Expression* arrlen = type->points_to()->array_type()->length(); + Expression* vallen = + Expression::make_slice_info(val, Expression::SLICE_INFO_LENGTH, + location); + vallen = Expression::make_set_and_use_temporary(vallen_temp, vallen, + location); + Expression* cond = Expression::make_binary(OPERATOR_GT, arrlen, vallen, + location); + + vallen = Expression::make_temporary_reference(vallen_temp, location); + Expression* panic = Runtime::make_call(Runtime::PANIC_SLICE_CONVERT, + location, 2, arrlen, vallen); + + Expression* nil = Expression::make_nil(location); + Expression* check = Expression::make_conditional(cond, panic, nil, + location); + + if (val_temp == NULL) + val = val->copy(); + else + val = Expression::make_temporary_reference(val_temp, location); + Expression* ptr = + Expression::make_slice_info(val, Expression::SLICE_INFO_VALUE_POINTER, + location); + ptr = Expression::make_unsafe_cast(type, ptr, location); + + return Expression::make_compound(check, ptr, location); + } + return this; } diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 231d92d..fad8ceb 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -582,6 +582,11 @@ DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C, "runtime.goPanicExtendSlice3C", DEF_GO_RUNTIME(PANIC_EXTEND_SLICE3_C_U, "runtime.goPanicExtendSlice3CU", P2(UINT64, INT), R0()) +// Panic for conversion of slice to pointer-to-array if the slice is +// too short. +DEF_GO_RUNTIME(PANIC_SLICE_CONVERT, "runtime.goPanicSliceConvert", + P2(INT, INT), R0()) + // Remove helper macros. #undef ABFT6 #undef ABFT2 diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index ab7166b..7c7b2eb 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -842,6 +842,13 @@ Type::are_convertible(const Type* lhs, const Type* rhs, std::string* reason) return true; } + // A slice may be converted to a pointer-to-array. + if (rhs->is_slice_type() + && lhs->points_to() != NULL + && lhs->points_to()->array_type() != NULL + && !lhs->points_to()->is_slice_type()) + return true; + // An unsafe.Pointer type may be converted to any pointer type or to // a type whose underlying type is uintptr, and vice-versa. if (lhs->is_unsafe_pointer_type() -- cgit v1.1