diff options
author | Martin Liska <mliska@suse.cz> | 2021-08-03 15:48:56 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2021-08-03 15:48:56 +0200 |
commit | fc45f824a020dff1ec2ea68cef1d23345fb7d447 (patch) | |
tree | 9786c97dbc9f824b0a6ad80527cd296d5d92e3ef /gcc/go | |
parent | 993a6fb652c7b82ef4fa8a3fdc0dbfe05987d829 (diff) | |
parent | 048039c49b96875144f67e7789fdea54abf7710b (diff) | |
download | gcc-fc45f824a020dff1ec2ea68cef1d23345fb7d447.zip gcc-fc45f824a020dff1ec2ea68cef1d23345fb7d447.tar.gz gcc-fc45f824a020dff1ec2ea68cef1d23345fb7d447.tar.bz2 |
Merge branch 'master' into devel/sphinx
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 280 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 4 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 40 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 8 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 11 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 58 | ||||
-rw-r--r-- | gcc/go/gofrontend/unsafe.cc | 16 |
8 files changed, 415 insertions, 4 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 4d0f44f..95b9340 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -adcf10890833026437a94da54934ce50c0018309 +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 a0472ac..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; } @@ -8252,12 +8301,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 +8747,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 +9947,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 +10058,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 +10134,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 +10555,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 +10671,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/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/runtime.def b/gcc/go/gofrontend/runtime.def index ec01be0..fad8ceb 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()) @@ -576,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 d08cbc9..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() @@ -10416,6 +10423,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_); } 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(); |