diff options
Diffstat (limited to 'gcc/fortran/expr.c')
-rw-r--r-- | gcc/fortran/expr.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/gcc/fortran/expr.c b/gcc/fortran/expr.c index ed639a7..bb183d4 100644 --- a/gcc/fortran/expr.c +++ b/gcc/fortran/expr.c @@ -4160,6 +4160,60 @@ gfc_has_default_initializer (gfc_symbol *der) } +/* + Generate an initializer expression which initializes the entirety of a union. + A normal structure constructor is insufficient without undue effort, because + components of maps may be oddly aligned/overlapped. (For example if a + character is initialized from one map overtop a real from the other, only one + byte of the real is actually initialized.) Unfortunately we don't know the + size of the union right now, so we can't generate a proper initializer, but + we use a NULL expr as a placeholder and do the right thing later in + gfc_trans_subcomponent_assign. + */ +static gfc_expr * +generate_union_initializer (gfc_component *un) +{ + if (un == NULL || un->ts.type != BT_UNION) + return NULL; + + gfc_expr *placeholder = gfc_get_null_expr (&un->loc); + placeholder->ts = un->ts; + return placeholder; +} + + +/* Get the user-specified initializer for a union, if any. This means the user + has said to initialize component(s) of a map. For simplicity's sake we + only allow the user to initialize the first map. We don't have to worry + about overlapping initializers as they are released early in resolution (see + resolve_fl_struct). */ + +static gfc_expr * +get_union_initializer (gfc_symbol *union_type, gfc_component **map_p) +{ + gfc_component *map; + gfc_expr *init=NULL; + + if (!union_type || union_type->attr.flavor != FL_UNION) + return NULL; + + for (map = union_type->components; map; map = map->next) + { + if (gfc_has_default_initializer (map->ts.u.derived)) + { + init = gfc_default_initializer (&map->ts); + if (map_p) + *map_p = map; + break; + } + } + + if (map_p && !init) + *map_p = NULL; + + return init; +} + /* Fetch or generate an initializer for the given component. Only generate an initializer if generate is true. */ @@ -4177,6 +4231,43 @@ component_initializer (gfc_typespec *ts, gfc_component *c, bool generate) if (c->ts.type == BT_DERIVED || c->ts.type == BT_CLASS) init = gfc_generate_initializer (&c->ts, true); + else if (c->ts.type == BT_UNION && c->ts.u.derived->components) + { + gfc_component *map = NULL; + gfc_constructor *ctor; + gfc_expr *user_init; + + /* If we don't have a user initializer and we aren't generating one, this + union has no initializer. */ + user_init = get_union_initializer (c->ts.u.derived, &map); + if (!user_init && !generate) + return NULL; + + /* Otherwise use a structure constructor. */ + init = gfc_get_structure_constructor_expr (c->ts.type, c->ts.kind, + &c->loc); + init->ts = c->ts; + + /* If we are to generate an initializer for the union, add a constructor + which initializes the whole union first. */ + if (generate) + { + ctor = gfc_constructor_get (); + ctor->expr = generate_union_initializer (c); + gfc_constructor_append (&init->value.constructor, ctor); + } + + /* If we found an initializer in one of our maps, apply it. Note this + is applied _after_ the entire-union initializer above if any. */ + if (user_init) + { + ctor = gfc_constructor_get (); + ctor->expr = user_init; + ctor->n.component = map; + gfc_constructor_append (&init->value.constructor, ctor); + } + } + /* Treat simple components like locals. */ else { |