diff options
author | Ian Lance Taylor <iant@golang.org> | 2023-10-19 19:34:31 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2023-12-18 17:30:56 -0800 |
commit | c20328e7cad2989bcdc9ff5003d6a16405c31ab5 (patch) | |
tree | 837db6d6d15e763947916017b122788e30ea1e59 /gcc/go/gofrontend/expressions.h | |
parent | 15cb5204e4c5f79d1b7179ae2590bb65e24b745f (diff) | |
download | gcc-c20328e7cad2989bcdc9ff5003d6a16405c31ab5.zip gcc-c20328e7cad2989bcdc9ff5003d6a16405c31ab5.tar.gz gcc-c20328e7cad2989bcdc9ff5003d6a16405c31ab5.tar.bz2 |
compiler: move lowering pass after check types pass
This change moves the lowering pass after the type determination and
the type checking passes. This lets us simplify some of the code that
determines the type of an expression, which previously had to work
correctly both before and after type determination.
I'm doing this to help with future generic support. For example, with
generics, we can see code like
func ident[T any](v T) T { return v }
func F() int32 {
s := int32(1)
return ident(s)
}
Before this change, we would type check return statements in the
lowering pass (see Return_statement::do_lower). With a generic
example like the above, that means we have to determine the type of s,
and use that to infer the type arguments passed to ident, and use that
to determine the result type of ident. That is too much to do at
lowering time. Of course we can change the way that return statements
work, but similar issues arise with index expressions, the types of
closures for function literals, and probably other cases as well.
Rather than try to deal with all those cases, we move the lowering
pass after type checking. This requires a bunch of changes, notably
for determining constant types. We have to add type checking for
various constructs that formerly disappeared in the lowering pass.
So it's a lot of shuffling. Sorry for the size of the patch.
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/536643
Diffstat (limited to 'gcc/go/gofrontend/expressions.h')
-rw-r--r-- | gcc/go/gofrontend/expressions.h | 236 |
1 files changed, 183 insertions, 53 deletions
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index d5df724..8763772 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -596,7 +596,8 @@ class Expression { return this->do_is_static_initializer(); } // If this is not a numeric constant, return false. If it is one, - // return true, and set VAL to hold the value. + // return true, and set VAL to hold the value. This method can be + // called before the determine_types pass. bool numeric_constant_value(Numeric_constant* val) { return this->do_numeric_constant_value(val); } @@ -633,8 +634,7 @@ class Expression // Return whether this expression really represents a type. bool - is_type_expression() const - { return this->classification_ == EXPRESSION_TYPE; } + is_type_expression() const; // If this is a const reference, return the Const_expression // structure. Otherwise, return NULL. This is a controlled dynamic @@ -735,6 +735,10 @@ class Expression unary_expression() { return this->convert<Unary_expression, EXPRESSION_UNARY>(); } + const Unary_expression* + unary_expression() const + { return this->convert<const Unary_expression, EXPRESSION_UNARY>(); } + // If this is a binary expression, return the Binary_expression // structure. Otherwise return NULL. Binary_expression* @@ -1022,10 +1026,8 @@ class Expression void determine_type_no_context(Gogo*); - // Return the current type of the expression. This may be changed - // by determine_type. This should not be called before the lowering - // pass, unless the is_type_expression method returns true (i.e., - // this is an EXPRESSION_TYPE). + // Return the type of the expression. This should not be called + // before the determine_types pass. Type* type() { return this->do_type(); } @@ -1472,17 +1474,6 @@ class Parser_expression : public Expression virtual Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int) = 0; - Type* - do_type(); - - void - do_determine_type(Gogo*, const Type_context*) - { go_unreachable(); } - - void - do_check_types(Gogo*) - { go_unreachable(); } - Bexpression* do_get_backend(Translate_context*) { go_unreachable(); } @@ -1495,7 +1486,8 @@ class Const_expression : public Expression public: Const_expression(Named_object* constant, Location location) : Expression(EXPRESSION_CONST_REFERENCE, location), - constant_(constant), type_(NULL), seen_(false) + constant_(constant), type_(NULL), iota_value_(0), seen_(false), + is_iota_(false) { } Named_object* @@ -1510,6 +1502,10 @@ class Const_expression : public Expression void check_for_init_loop(); + // Set the iota value if this is a reference to iota. + void + set_iota_value(int); + protected: int do_traverse(Traverse*); @@ -1576,9 +1572,14 @@ class Const_expression : public Expression // The type of this reference. This is used if the constant has an // abstract type. Type* type_; + // If this const is a reference to the predeclared iota value, the + // value to use. + int iota_value_; // Used to prevent infinite recursion when a constant incorrectly // refers to itself. mutable bool seen_; + // Whether this const is a reference to the predeclared iota value. + bool is_iota_; }; // An expression which is simply a variable. @@ -1976,8 +1977,7 @@ class Type_conversion_expression : public Expression do_boolean_constant_value(bool*); Type* - do_type() - { return this->type_; } + do_type(); void do_determine_type(Gogo*, const Type_context*); @@ -2074,7 +2074,7 @@ class Unary_expression : public Expression Unary_expression(Operator op, Expression* expr, Location location) : Expression(EXPRESSION_UNARY, location), op_(op), escapes_(true), create_temp_(false), is_gc_root_(false), - is_slice_init_(false), expr_(expr), + is_slice_init_(false), expr_(expr), type_(NULL), issue_nil_check_(NIL_CHECK_DEFAULT) { } @@ -2131,7 +2131,7 @@ class Unary_expression : public Expression // could be done, false if not. On overflow, issues an error and // sets *ISSUED_ERROR. static bool - eval_constant(Operator op, const Numeric_constant* unc, + eval_constant(Type*, Operator op, const Numeric_constant* unc, Location, Numeric_constant* nc, bool *issued_error); static Expression* @@ -2246,6 +2246,8 @@ class Unary_expression : public Expression bool is_slice_init_; // The operand. Expression* expr_; + // The type of the expression. Not used for AND and MULT. + Type* type_; // Whether or not to issue a nil check for this expression if its address // is being taken. Nil_check_classification issue_nil_check_; @@ -2372,15 +2374,15 @@ class Binary_expression : public Expression static bool eval_integer(Operator op, const Numeric_constant*, const Numeric_constant*, - Location, Numeric_constant*); + Location, Numeric_constant*, bool* issued_error); static bool eval_float(Operator op, const Numeric_constant*, const Numeric_constant*, - Location, Numeric_constant*); + Location, Numeric_constant*, bool* issued_error); static bool eval_complex(Operator op, const Numeric_constant*, const Numeric_constant*, - Location, Numeric_constant*); + Location, Numeric_constant*, bool* issued_error); static bool compare_integer(const Numeric_constant*, const Numeric_constant*, int*); @@ -2412,7 +2414,7 @@ class Binary_expression : public Expression Expression* left_; // The right hand side operand. Expression* right_; - // The type of a comparison operation. + // The type of the expression. Type* type_; }; @@ -2493,8 +2495,8 @@ class Call_expression : public Expression Call_expression(Expression* fn, Expression_list* args, bool is_varargs, Location location) : Expression(EXPRESSION_CALL, location), - fn_(fn), args_(args), type_(NULL), call_(NULL), call_temp_(NULL) - , expected_result_count_(0), is_varargs_(is_varargs), + fn_(fn), args_(args), type_(NULL), lowered_(NULL), call_(NULL), + call_temp_(NULL), expected_result_count_(0), is_varargs_(is_varargs), varargs_are_lowered_(false), types_are_determined_(false), is_deferred_(false), is_concurrent_(false), is_equal_function_(false), issued_error_(false), is_multi_value_arg_(false), is_flattened_(false) @@ -2530,7 +2532,8 @@ class Call_expression : public Expression // Set the number of results expected from this call. This is used // when the call appears in a context that expects multiple results, - // such as a, b = f(). + // such as a, b = f(). This must be called before the + // determine_types pass. void set_expected_result_count(size_t); @@ -2616,6 +2619,10 @@ class Call_expression : public Expression inline const Builtin_call_expression* builtin_call_expression() const; + // Lower to a Builtin_call_expression if appropriate. + Expression* + lower_builtin(Gogo*); + protected: int do_traverse(Traverse*); @@ -2627,12 +2634,20 @@ class Call_expression : public Expression do_flatten(Gogo*, Named_object*, Statement_inserter*); bool - do_discarding_value() - { return true; } + do_discarding_value(); virtual Type* do_type(); + virtual bool + do_is_constant() const; + + bool + do_is_untyped(Type**) const; + + bool + do_numeric_constant_value(Numeric_constant*); + virtual void do_determine_type(Gogo*, const Type_context*); @@ -2665,17 +2680,26 @@ class Call_expression : public Expression set_args(Expression_list* args) { this->args_ = args; } - // Let a builtin expression lower varargs. - void - lower_varargs(Gogo*, Named_object* function, Statement_inserter* inserter, - Type* varargs_type, size_t param_count, - Slice_storage_escape_disp escape_disp); - // Let a builtin expression check whether types have been // determined. bool determining_types(); + // Let a builtin expression retrieve the expected type. + Type* + type() + { return this->type_; } + + // Let a builtin expression set the expected type. + void + set_type(Type* type) + { this->type_ = type; } + + // Let a builtin expression simply f(g()) where g returns multiple + // results. + void + simplify_multiple_results(Gogo*); + void export_arguments(Export_function_body*) const; @@ -2687,7 +2711,10 @@ class Call_expression : public Expression private: bool - check_argument_type(int, const Type*, const Type*, Location, bool); + rewrite_varargs(); + + bool + check_argument_type(int, const Type*, const Type*, Location); Expression* intrinsify(Gogo*, Statement_inserter*); @@ -2706,6 +2733,8 @@ class Call_expression : public Expression Expression_list* args_; // The type of the expression, to avoid recomputing it. Type* type_; + // If not NULL, this is a lowered version of this Call_expression. + Expression* lowered_; // The backend expression for the call, used for a call which returns a tuple. Bexpression* call_; // A temporary variable to store this call if the function returns a tuple. @@ -3087,7 +3116,8 @@ class Unknown_expression : public Parser_expression public: Unknown_expression(Named_object* named_object, Location location) : Parser_expression(EXPRESSION_UNKNOWN_REFERENCE, location), - named_object_(named_object), no_error_message_(false) + named_object_(named_object), lowered_(NULL), iota_value_(0), + no_error_message_(false), is_iota_(false) { } // The associated named object. @@ -3106,7 +3136,38 @@ class Unknown_expression : public Parser_expression set_no_error_message() { this->no_error_message_ = true; } + // Set the iota value if this is a reference to iota. + void + set_iota_value(int); + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(Gogo*, const Type_context*); + + bool + do_is_constant() const; + + bool + do_is_untyped(Type**) const; + + virtual bool + do_numeric_constant_value(Numeric_constant*); + + virtual bool + do_string_constant_value(std::string*); + + virtual bool + do_boolean_constant_value(bool*); + + bool + do_is_addressable() const; + Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); @@ -3120,9 +3181,15 @@ class Unknown_expression : public Parser_expression private: // The unknown name. Named_object* named_object_; + // The fully resolved expression. + Expression* lowered_; + // The iota value if this turns out to be a reference to iota. + int iota_value_; // True if we should not give errors if this is undefined. This is // used if there was a parse failure. bool no_error_message_; + // True if this could be a reference to iota. + bool is_iota_; }; // An index expression. This is lowered to an array index, a string @@ -3134,9 +3201,15 @@ class Index_expression : public Parser_expression Index_expression(Expression* left, Expression* start, Expression* end, Expression* cap, Location location) : Parser_expression(EXPRESSION_INDEX, location), - left_(left), start_(start), end_(end), cap_(cap) + left_(left), start_(start), end_(end), cap_(cap), + needs_nil_check_(false) { } + // Return the expression being indexed. + Expression* + left() const + { return this->left_; } + // Dump an index expression, i.e. an expression of the form // expr[expr], expr[expr:expr], or expr[expr:expr:expr] to a dump context. static void @@ -3144,24 +3217,44 @@ class Index_expression : public Parser_expression const Expression* start, const Expression* end, const Expression* cap); + // Report whether EXPR is a map index expression. + static bool + is_map_index(Expression* expr); + protected: int do_traverse(Traverse*); + Type* + do_type(); + + void + do_determine_type(Gogo*, const Type_context*); + + void + do_check_types(Gogo*); + + bool + do_is_addressable() const; + Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); Expression* do_copy() { - return new Index_expression(this->left_->copy(), this->start_->copy(), - (this->end_ == NULL - ? NULL - : this->end_->copy()), - (this->cap_ == NULL - ? NULL - : this->cap_->copy()), - this->location()); + Index_expression* ret = + new Index_expression(this->left_->copy(), this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + (this->cap_ == NULL + ? NULL + : this->cap_->copy()), + this->location()); + if (this->needs_nil_check_) + ret->needs_nil_check_ = true; + return ret; } // This shouldn't be called--we don't know yet. @@ -3173,8 +3266,8 @@ class Index_expression : public Parser_expression do_dump_expression(Ast_dump_context*) const; void - do_issue_nil_check() - { this->left_->issue_nil_check(); } + do_issue_nil_check(); + private: // The expression being indexed. Expression* left_; @@ -3187,6 +3280,9 @@ class Index_expression : public Parser_expression // default capacity, non-NULL for indices and slices that specify the // capacity. Expression* cap_; + // True if this needs a nil check. This changes how we handle + // dereferencing a pointer to an array. + bool needs_nil_check_; }; // An array index. This is used for both indexing and slicing. @@ -3234,6 +3330,11 @@ class Array_index_expression : public Expression set_needs_bounds_check(bool b) { this->needs_bounds_check_ = b; } + // Check indexes. + static bool + check_indexes(Expression* array, Expression* start, Expression* len, + Expression* cap, Location); + protected: int do_traverse(Traverse*); @@ -3339,6 +3440,11 @@ class String_index_expression : public Expression end() const { return this->end_; } + // Check indexes. + static bool + check_indexes(Expression* string, Expression* start, Expression* len, + Location); + protected: int do_traverse(Traverse*); @@ -3832,7 +3938,8 @@ class Composite_literal_expression : public Parser_expression Location location) : Parser_expression(EXPRESSION_COMPOSITE_LITERAL, location), type_(type), depth_(depth), vals_(vals), has_keys_(has_keys), - all_are_names_(all_are_names), key_path_(std::vector<bool>(depth)) + all_are_names_(all_are_names), key_path_(std::vector<bool>(depth)), + traverse_order_(NULL) {} @@ -3848,6 +3955,15 @@ class Composite_literal_expression : public Parser_expression int do_traverse(Traverse* traverse); + Type* + do_type(); + + void + do_determine_type(Gogo*, const Type_context*); + + void + do_check_types(Gogo*); + Expression* do_lower(Gogo*, Named_object*, Statement_inserter*, int); @@ -3858,8 +3974,14 @@ class Composite_literal_expression : public Parser_expression do_dump_expression(Ast_dump_context*) const; private: + bool + resolve_struct_keys(Gogo*, Type* type); + + void + resolve_array_length(Type*); + Expression* - lower_struct(Gogo*, Type*); + lower_struct(Type*); Expression* lower_array(Type*); @@ -3888,6 +4010,10 @@ class Composite_literal_expression : public Parser_expression // a value. This is used to decide which type to use when given a map literal // with omitted key types. std::vector<bool> key_path_; + // If not NULL, the order in which to traverse vals_ for a struct + // composite literal. This is used so that we implement the order + // of evaluation rules correctly. + std::vector<unsigned long>* traverse_order_; }; // Helper/mixin class for struct and array construction expressions; @@ -3948,6 +4074,10 @@ class Struct_construction_expression : public Expression, bool is_constant_struct() const; + // Check types of a struct composite literal. + static bool + check_value_types(Gogo*, Type*, Expression_list*, Location); + protected: int do_traverse(Traverse* traverse); |