aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2021-08-03 15:48:56 +0200
committerMartin Liska <mliska@suse.cz>2021-08-03 15:48:56 +0200
commitfc45f824a020dff1ec2ea68cef1d23345fb7d447 (patch)
tree9786c97dbc9f824b0a6ad80527cd296d5d92e3ef /gcc/go
parent993a6fb652c7b82ef4fa8a3fdc0dbfe05987d829 (diff)
parent048039c49b96875144f67e7789fdea54abf7710b (diff)
downloadgcc-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/MERGE2
-rw-r--r--gcc/go/gofrontend/expressions.cc280
-rw-r--r--gcc/go/gofrontend/expressions.h4
-rw-r--r--gcc/go/gofrontend/gogo.cc40
-rw-r--r--gcc/go/gofrontend/gogo.h8
-rw-r--r--gcc/go/gofrontend/runtime.def11
-rw-r--r--gcc/go/gofrontend/types.cc58
-rw-r--r--gcc/go/gofrontend/unsafe.cc16
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();