-c4adba240f9d5af8ab0534316d6b05bd988c432c
+29c61dc3c5151df5de9362b7882ccf04679df976
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
}
Type* type = first_arg->type();
+ if (!type->in_heap())
+ go_error_at(first_arg->location(),
+ "can't make slice of go:notinheap type");
+
bool is_slice = false;
bool is_map = false;
bool is_chan = false;
}
Type* element_type = slice_type->array_type()->element_type();
+ if (!element_type->in_heap())
+ go_error_at(args->front()->location(),
+ "can't append to slice of go:notinheap type");
if (this->is_varargs())
{
if (!args->back()->type()->is_slice_type()
return Type::make_pointer_type(this->type_);
}
+void
+Allocation_expression::do_check_types(Gogo*)
+{
+ if (!this->type_->in_heap())
+ go_error_at(this->location(), "can't heap allocate go:notinheap type");
+}
+
// Make a copy of an allocation expression.
Expression*
do_determine_type(const Type_context*)
{ }
+ void
+ do_check_types(Gogo*);
+
Expression*
do_copy();
// Applies to the next function. Do not inline the function.
this->pragmas_ |= GOPRAGMA_NOINLINE;
}
+ else if (verb == "go:notinheap")
+ {
+ // Applies to the next type. The type does not live in the heap.
+ this->pragmas_ |= GOPRAGMA_NOTINHEAP;
+ }
else if (verb == "go:systemstack")
{
// Applies to the next function. It must run on the system stack.
GOPRAGMA_NOWRITEBARRIER = 1 << 6, // No write barriers.
GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees.
GOPRAGMA_CGOUNSAFEARGS = 1 << 8, // Pointer to arg is pointer to all.
- GOPRAGMA_UINTPTRESCAPES = 1 << 9 // uintptr(p) escapes.
+ GOPRAGMA_UINTPTRESCAPES = 1 << 9, // uintptr(p) escapes.
+ GOPRAGMA_NOTINHEAP = 1 << 10 // type is not in heap.
};
// A token returned from the lexer.
const Token* token = this->peek_token();
unsigned int pragmas = this->lex_->get_and_clear_pragmas();
- if (pragmas != 0 && !token->is_keyword(KEYWORD_FUNC))
+ if (pragmas != 0
+ && !token->is_keyword(KEYWORD_FUNC)
+ && !token->is_keyword(KEYWORD_TYPE))
go_warning_at(token->location(), 0,
"ignoring magic comment before non-function");
if (token->is_keyword(KEYWORD_CONST))
this->const_decl();
else if (token->is_keyword(KEYWORD_TYPE))
- this->type_decl();
+ this->type_decl(pragmas);
else if (token->is_keyword(KEYWORD_VAR))
this->var_decl();
else if (token->is_keyword(KEYWORD_FUNC))
// Decl<P> = P | "(" [ List<P> ] ")" .
void
-Parse::decl(void (Parse::*pfn)(void*), void* varg)
+Parse::decl(void (Parse::*pfn)(void*, unsigned int), void* varg,
+ unsigned int pragmas)
{
if (this->peek_token()->is_eof())
{
}
if (!this->peek_token()->is_op(OPERATOR_LPAREN))
- (this->*pfn)(varg);
+ (this->*pfn)(varg, pragmas);
else
{
+ if (pragmas != 0)
+ go_warning_at(this->location(), 0,
+ "ignoring magic //go:... comment before group");
if (!this->advance_token()->is_op(OPERATOR_RPAREN))
{
this->list(pfn, varg, true);
// might follow. This is either a '}' or a ')'.
void
-Parse::list(void (Parse::*pfn)(void*), void* varg, bool follow_is_paren)
+Parse::list(void (Parse::*pfn)(void*, unsigned int), void* varg,
+ bool follow_is_paren)
{
- (this->*pfn)(varg);
+ (this->*pfn)(varg, 0);
Operator follow = follow_is_paren ? OPERATOR_RPAREN : OPERATOR_RCURLY;
while (this->peek_token()->is_op(OPERATOR_SEMICOLON)
|| this->peek_token()->is_op(OPERATOR_COMMA))
go_error_at(this->location(), "unexpected comma");
if (this->advance_token()->is_op(follow))
break;
- (this->*pfn)(varg);
+ (this->*pfn)(varg, 0);
}
}
// TypeDecl = "type" Decl<TypeSpec> .
void
-Parse::type_decl()
+Parse::type_decl(unsigned int pragmas)
{
go_assert(this->peek_token()->is_keyword(KEYWORD_TYPE));
this->advance_token();
- this->decl(&Parse::type_spec, NULL);
+ this->decl(&Parse::type_spec, NULL, pragmas);
}
// TypeSpec = identifier ["="] Type .
void
-Parse::type_spec(void*)
+Parse::type_spec(void*, unsigned int pragmas)
{
const Token* token = this->peek_token();
if (!token->is_identifier())
this->gogo_->define_type(named_type, nt);
go_assert(named_type->package() == NULL);
+
+ if ((pragmas & GOPRAGMA_NOTINHEAP) != 0)
+ {
+ nt->set_not_in_heap();
+ pragmas &= ~GOPRAGMA_NOTINHEAP;
+ }
+ if (pragmas != 0)
+ go_warning_at(location, 0,
+ "ignoring magic //go:... comment before type");
}
else
{
{
go_assert(this->peek_token()->is_keyword(KEYWORD_VAR));
this->advance_token();
- this->decl(&Parse::var_spec, NULL);
+ this->decl(&Parse::var_spec, NULL, 0);
}
// VarSpec = IdentifierList
// ( CompleteType [ "=" ExpressionList ] | "=" ExpressionList ) .
void
-Parse::var_spec(void*)
+Parse::var_spec(void*, unsigned int pragmas)
{
+ if (pragmas != 0)
+ go_warning_at(this->location(), 0,
+ "ignoring magic //go:... comment before var");
+
// Get the variable names.
Typed_identifier_list til;
this->identifier_list(&til);
{
go_assert(this->peek_token()->is_keyword(KEYWORD_IMPORT));
this->advance_token();
- this->decl(&Parse::import_spec, NULL);
+ this->decl(&Parse::import_spec, NULL, 0);
}
// ImportSpec = [ "." | PackageName ] PackageFileName .
void
-Parse::import_spec(void*)
+Parse::import_spec(void*, unsigned int pragmas)
{
+ if (pragmas != 0)
+ go_warning_at(this->location(), 0,
+ "ignoring magic //go:... comment before import");
+
const Token* token = this->peek_token();
Location location = token->location();
void method_spec(Typed_identifier_list*);
void declaration();
bool declaration_may_start_here();
- void decl(void (Parse::*)(void*), void*);
- void list(void (Parse::*)(void*), void*, bool);
+ void decl(void (Parse::*)(void*, unsigned int), void*, unsigned int pragmas);
+ void list(void (Parse::*)(void*, unsigned int), void*, bool);
void const_decl();
void const_spec(Type**, Expression_list**);
- void type_decl();
- void type_spec(void*);
+ void type_decl(unsigned int pragmas);
+ void type_spec(void*, unsigned int pragmas);
void var_decl();
- void var_spec(void*);
+ void var_spec(void*, unsigned int pragmas);
void init_vars(const Typed_identifier_list*, Type*, Expression_list*,
bool is_coloneq, Location);
bool init_vars_from_call(const Typed_identifier_list*, Type*, Expression*,
void goto_stat();
void package_clause();
void import_decl();
- void import_spec(void*);
+ void import_spec(void*, unsigned int pragmas);
void reset_iota();
int iota_value();
if (Type::are_assignable(lhs, rhs, reason))
return true;
+ // A pointer to a regular type may not be converted to a pointer to
+ // a type that may not live in the heap, except when converting to
+ // unsafe.Pointer.
+ if (lhs->points_to() != NULL
+ && rhs->points_to() != NULL
+ && !rhs->points_to()->in_heap()
+ && lhs->points_to()->in_heap()
+ && !lhs->is_unsafe_pointer_type())
+ {
+ if (reason != NULL)
+ reason->assign(_("conversion from notinheap type to normal type"));
+ return false;
+ }
+
// The types are convertible if they have identical underlying
// types, ignoring struct field tags.
if ((lhs->named_type() != NULL || rhs->named_type() != NULL)
return false;
}
+// Return whether this struct type is permitted to be in the heap.
+
+bool
+Struct_type::do_in_heap()
+{
+ const Struct_field_list* fields = this->fields_;
+ if (fields == NULL)
+ return true;
+ for (Struct_field_list::const_iterator pf = fields->begin();
+ pf != fields->end();
+ ++pf)
+ {
+ if (!pf->type()->in_heap())
+ return false;
+ }
+ return true;
+}
+
// Build identity and hash functions for this struct.
// Hash code.
// The runtime support uses "map[void]void".
if (!this->key_type_->is_comparable() && !this->key_type_->is_void_type())
go_error_at(this->location_, "invalid map key type");
+ if (!this->key_type_->in_heap())
+ go_error_at(this->location_, "go:notinheap map key not allowed");
+ if (!this->val_type_->in_heap())
+ go_error_at(this->location_, "go:notinheap map value not allowed");
return true;
}
// Class Channel_type.
+// Verify.
+
+bool
+Channel_type::do_verify()
+{
+ // We have no location for this error, but this is not something the
+ // ordinary user will see.
+ if (!this->element_type_->in_heap())
+ go_error_at(Linemap::unknown_location(),
+ "chan of go:notinheap type not allowed");
+ return true;
+}
+
// Hash code.
unsigned int
needs_key_update()
{ return this->do_needs_key_update(); }
+ // Whether the type is permitted in the heap.
+ bool
+ in_heap()
+ { return this->do_in_heap(); }
+
// Return a hash code for this type for the method hash table.
// Types which are equivalent according to are_identical will have
// the same hash code.
do_needs_key_update()
{ return false; }
+ virtual bool
+ do_in_heap()
+ { return true; }
+
virtual unsigned int
do_hash_for_method(Gogo*) const;
// The GC symbol for this type. This starts out as NULL and
// is filled in as needed.
Bvariable* gc_symbol_var_;
+ // Whether this type can appear in the heap.
+ bool in_heap_;
};
// Type hash table operations.
bool
do_needs_key_update();
+ bool
+ do_in_heap();
+
unsigned int
do_hash_for_method(Gogo*) const;
do_needs_key_update()
{ return this->element_type_->needs_key_update(); }
+ bool
+ do_in_heap()
+ { return this->length_ == NULL || this->element_type_->in_heap(); }
+
unsigned int
do_hash_for_method(Gogo*) const;
do_traverse(Traverse* traverse)
{ return Type::traverse(this->element_type_, traverse); }
+ bool
+ do_verify();
+
bool
do_has_pointer() const
{ return true; }
type_(type), local_methods_(NULL), all_methods_(NULL),
interface_method_tables_(NULL), pointer_interface_method_tables_(NULL),
location_(location), named_btype_(NULL), dependencies_(),
- is_alias_(false), is_visible_(true), is_error_(false),
+ is_alias_(false), is_visible_(true), is_error_(false), in_heap_(true),
is_placeholder_(false), is_converted_(false), is_circular_(false),
is_verified_(false), seen_(false), seen_in_compare_is_identity_(false),
seen_in_get_backend_(false), seen_alias_(false)
set_is_alias()
{ this->is_alias_ = true; }
+ // Mark this type as not permitted in the heap.
+ void
+ set_not_in_heap()
+ { this->in_heap_ = false; }
+
// Return the function in which this type is defined. This will
// return NULL for a type defined in global scope.
const Named_object*
bool
do_needs_key_update();
+ bool
+ do_in_heap()
+ { return this->in_heap_ && this->type_->in_heap(); }
+
unsigned int
do_hash_for_method(Gogo*) const;
bool is_visible_;
// Whether this type is erroneous.
bool is_error_;
+ // Whether this type is permitted in the heap. This is true by
+ // default, false if there is a magic //go:notinheap comment.
+ bool in_heap_;
// Whether the current value of named_btype_ is a placeholder for
// which the final size of the type is not known.
bool is_placeholder_;
do_needs_key_update()
{ return this->real_type()->needs_key_update(); }
+ bool
+ do_in_heap()
+ { return this->real_type()->in_heap(); }
+
unsigned int
do_hash_for_method(Gogo* gogo) const
{ return this->real_type()->hash_for_method(gogo); }
if (!var->has_pre_init() && init->is_static_initializer())
return TRAVERSE_CONTINUE;
+ // Nothing to do for a type that can not be in the heap, or a
+ // pointer to a type that can not be in the heap.
+ if (!var->type()->in_heap())
+ return TRAVERSE_CONTINUE;
+ if (var->type()->points_to() != NULL && !var->type()->points_to()->in_heap())
+ return TRAVERSE_CONTINUE;
+
// Otherwise change the initializer into a pre_init assignment
// statement with a write barrier.
if (!var->type()->has_pointer())
break;
+ // Nothing to do for a type that can not be in the heap, or a
+ // pointer to a type that can not be in the heap.
+ if (!var->type()->in_heap())
+ break;
+ if (var->type()->points_to() != NULL
+ && !var->type()->points_to()->in_heap())
+ break;
+
// Otherwise initialize the variable with a write barrier.
Function* function = this->function_;
}
}
+ // Nothing to do for a type that can not be in the heap, or a
+ // pointer to a type that can not be in the heap.
+ if (!lhs->type()->in_heap())
+ return false;
+ if (lhs->type()->points_to() != NULL && !lhs->type()->points_to()->in_heap())
+ return false;
+
// Write barrier needed in other cases.
return true;
}