aboutsummaryrefslogtreecommitdiff
path: root/gcc/fortran/trans-expr.c
diff options
context:
space:
mode:
authorPaul Thomas <pault@gcc.gnu.org>2020-12-18 14:00:11 +0000
committerPaul Thomas <pault@gcc.gnu.org>2020-12-18 14:00:11 +0000
commitce8dcc9105cbd4043d575d8b2c91309a423951a9 (patch)
treecbdbfaf7af17a254b4191fb5935fbfbec8677016 /gcc/fortran/trans-expr.c
parent11f07ef37786d10517121fc6226681cd1aa2aea2 (diff)
downloadgcc-ce8dcc9105cbd4043d575d8b2c91309a423951a9.zip
gcc-ce8dcc9105cbd4043d575d8b2c91309a423951a9.tar.gz
gcc-ce8dcc9105cbd4043d575d8b2c91309a423951a9.tar.bz2
As well as the PR this patch fixes problems in handling class objects
2020-12-18 Paul Thomas <pault@gcc.gnu.org> gcc/fortran PR fortran/83118 PR fortran/96012 * resolve.c (resolve_ordinary_assign): Generate a vtable if necessary for scalar non-polymorphic rhs's to unlimited lhs's. * trans-array.c (get_class_info_from_ss): New function. (gfc_trans_allocate_array_storage): Defer obtaining class element type until all sources of class exprs are tried. Use class API rather than TREE_OPERAND. Look for class expressions in ss->info by calling get_class_info_from_ss. After, obtain the element size for class descriptors. Where the element type is unknown, cast the data as character(len=size) to overcome unlimited polymorphic problems. (gfc_conv_ss_descriptor): Do not fix class variable refs. (build_class_array_ref, structure_alloc_comps): Replace code replicating the new function gfc_resize_class_size_with_len. (gfc_alloc_allocatable_for_assignment): Obtain element size for lhs in cases of deferred characters and class enitities. Move code for the element size of rhs to start of block. Clean up extraction of class parameters throughout this function. After the shape check test whether or not the lhs and rhs element sizes are the same. Use earlier evaluation of 'cond_null'. Reallocation of lhs only to happen if size changes or element size changes. * trans-expr.c (gfc_resize_class_size_with_len): New function. (gfc_get_class_from_expr): If a constant expression is encountered, return NULL_TREE; (trans_scalar_class_assign): New function. (gfc_conv_procedure_call): Ensure the vtable is present for passing a non-class actual to an unlimited formal. (trans_class_vptr_len_assignment): For expressions of type BT_CLASS, extract the class expression if necessary. Use a statement block outside the loop body. Ensure that 'rhs' is of the correct type. Obtain rhs vptr in all circumstances. (gfc_trans_scalar_assign): Call trans_scalar_class_assign to make maximum use of the vptr copy in place of assignment. (trans_class_assignment): Actually do reallocation if needed. (gfc_trans_assignment_1): Simplify some of the logic with 'realloc_flag'. Set 'vptr_copy' for all array assignments to unlimited polymorphic lhs. * trans.c (gfc_build_array_ref): Call gfc_resize_class_size_ with_len to correct span for unlimited polymorphic decls. * trans.h : Add prototype for gfc_resize_class_size_with_len. gcc/testsuite/ PR fortran/83118 PR fortran/96012 * gfortran.dg/dependency_60.f90: New test. * gfortran.dg/class_allocate_25.f90: New test. * gfortran.dg/class_assign_4.f90: New test. * gfortran.dg/unlimited_polymorphic_32.f03: New test.
Diffstat (limited to 'gcc/fortran/trans-expr.c')
-rw-r--r--gcc/fortran/trans-expr.c264
1 files changed, 241 insertions, 23 deletions
diff --git a/gcc/fortran/trans-expr.c b/gcc/fortran/trans-expr.c
index 2167de4..bfe08be 100644
--- a/gcc/fortran/trans-expr.c
+++ b/gcc/fortran/trans-expr.c
@@ -257,6 +257,42 @@ gfc_class_len_or_zero_get (tree decl)
}
+tree
+gfc_resize_class_size_with_len (stmtblock_t * block, tree class_expr, tree size)
+{
+ tree tmp;
+ tree tmp2;
+ tree type;
+
+ tmp = gfc_class_len_or_zero_get (class_expr);
+
+ /* Include the len value in the element size if present. */
+ if (!integer_zerop (tmp))
+ {
+ type = TREE_TYPE (size);
+ if (block)
+ {
+ size = gfc_evaluate_now (size, block);
+ tmp = gfc_evaluate_now (fold_convert (type , tmp), block);
+ }
+ tmp2 = fold_build2_loc (input_location, MULT_EXPR,
+ type, size, tmp);
+ tmp = fold_build2_loc (input_location, GT_EXPR,
+ logical_type_node, tmp,
+ build_zero_cst (type));
+ size = fold_build3_loc (input_location, COND_EXPR,
+ type, tmp, tmp2, size);
+ }
+ else
+ return size;
+
+ if (block)
+ size = gfc_evaluate_now (size, block);
+
+ return size;
+}
+
+
/* Get the specified FIELD from the VPTR. */
static tree
@@ -483,6 +519,9 @@ gfc_get_class_from_expr (tree expr)
for (tmp = expr; tmp; tmp = TREE_OPERAND (tmp, 0))
{
+ if (CONSTANT_CLASS_P (tmp))
+ return NULL_TREE;
+
type = TREE_TYPE (tmp);
while (type)
{
@@ -1606,6 +1645,111 @@ gfc_trans_class_init_assign (gfc_code *code)
}
+/* Class valued elemental function calls or class array elements arriving
+ in gfc_trans_scalar_assign come here. Wherever possible the vptr copy
+ is used to ensure that the rhs dynamic type is assigned to the lhs. */
+
+static bool
+trans_scalar_class_assign (stmtblock_t *block, gfc_se *lse, gfc_se *rse)
+{
+ tree fcn;
+ tree rse_expr;
+ tree class_data;
+ tree tmp;
+ tree zero;
+ tree cond;
+ tree final_cond;
+ stmtblock_t inner_block;
+ bool is_descriptor;
+ bool not_call_expr = TREE_CODE (rse->expr) != CALL_EXPR;
+ bool not_lhs_array_type;
+
+ /* Temporaries arising from depencies in assignment get cast as a
+ character type of the dynamic size of the rhs. Use the vptr copy
+ for this case. */
+ tmp = TREE_TYPE (lse->expr);
+ not_lhs_array_type = !(tmp && TREE_CODE (tmp) == ARRAY_TYPE
+ && TYPE_MAX_VALUE (TYPE_DOMAIN (tmp)) != NULL_TREE);
+
+ /* Use ordinary assignment if the rhs is not a call expression or
+ the lhs is not a class entity or an array(ie. character) type. */
+ if ((not_call_expr && gfc_get_class_from_expr (lse->expr) == NULL_TREE)
+ && not_lhs_array_type)
+ return false;
+
+ /* Ordinary assignment can be used if both sides are class expressions
+ since the dynamic type is preserved by copying the vptr. This
+ should only occur, where temporaries are involved. */
+ if (GFC_CLASS_TYPE_P (TREE_TYPE (lse->expr))
+ && GFC_CLASS_TYPE_P (TREE_TYPE (rse->expr)))
+ return false;
+
+ /* Fix the class expression and the class data of the rhs. */
+ if (!GFC_CLASS_TYPE_P (TREE_TYPE (rse->expr))
+ || not_call_expr)
+ {
+ tmp = gfc_get_class_from_expr (rse->expr);
+ if (tmp == NULL_TREE)
+ return false;
+ rse_expr = gfc_evaluate_now (tmp, block);
+ }
+ else
+ rse_expr = gfc_evaluate_now (rse->expr, block);
+
+ class_data = gfc_class_data_get (rse_expr);
+
+ /* Check that the rhs data is not null. */
+ is_descriptor = GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (class_data));
+ if (is_descriptor)
+ class_data = gfc_conv_descriptor_data_get (class_data);
+ class_data = gfc_evaluate_now (class_data, block);
+
+ zero = build_int_cst (TREE_TYPE (class_data), 0);
+ cond = fold_build2_loc (input_location, NE_EXPR,
+ logical_type_node,
+ class_data, zero);
+
+ /* Copy the rhs to the lhs. */
+ fcn = gfc_vptr_copy_get (gfc_class_vptr_get (rse_expr));
+ fcn = build_fold_indirect_ref_loc (input_location, fcn);
+ tmp = gfc_evaluate_now (gfc_build_addr_expr (NULL, rse->expr), block);
+ tmp = is_descriptor ? tmp : class_data;
+ tmp = build_call_expr_loc (input_location, fcn, 2, tmp,
+ gfc_build_addr_expr (NULL, lse->expr));
+ gfc_add_expr_to_block (block, tmp);
+
+ /* Only elemental function results need to be finalised and freed. */
+ if (not_call_expr)
+ return true;
+
+ /* Finalize the class data if needed. */
+ gfc_init_block (&inner_block);
+ fcn = gfc_vptr_final_get (gfc_class_vptr_get (rse_expr));
+ zero = build_int_cst (TREE_TYPE (fcn), 0);
+ final_cond = fold_build2_loc (input_location, NE_EXPR,
+ logical_type_node, fcn, zero);
+ fcn = build_fold_indirect_ref_loc (input_location, fcn);
+ tmp = build_call_expr_loc (input_location, fcn, 1, class_data);
+ tmp = build3_v (COND_EXPR, final_cond,
+ tmp, build_empty_stmt (input_location));
+ gfc_add_expr_to_block (&inner_block, tmp);
+
+ /* Free the class data. */
+ tmp = gfc_call_free (class_data);
+ tmp = build3_v (COND_EXPR, cond, tmp,
+ build_empty_stmt (input_location));
+ gfc_add_expr_to_block (&inner_block, tmp);
+
+ /* Finish the inner block and subject it to the condition on the
+ class data being non-zero. */
+ tmp = gfc_finish_block (&inner_block);
+ tmp = build3_v (COND_EXPR, cond, tmp,
+ build_empty_stmt (input_location));
+ gfc_add_expr_to_block (block, tmp);
+
+ return true;
+}
+
/* End of prototype trans-class.c */
@@ -5613,8 +5757,10 @@ gfc_conv_procedure_call (gfc_se * se, gfc_symbol * sym,
{
/* The intrinsic type needs to be converted to a temporary
CLASS object for the unlimited polymorphic formal. */
+ gfc_find_vtab (&e->ts);
gfc_init_se (&parmse, se);
gfc_conv_intrinsic_to_class (&parmse, e, fsym->ts);
+
}
else if (se->ss && se->ss->info->useflags)
{
@@ -8926,14 +9072,32 @@ trans_class_vptr_len_assignment (stmtblock_t *block, gfc_expr * le,
tree tmp, to_len = NULL_TREE, from_len = NULL_TREE, lhs_vptr;
bool set_vptr = false, temp_rhs = false;
stmtblock_t *pre = block;
+ tree class_expr = NULL_TREE;
/* Create a temporary for complicated expressions. */
if (re->expr_type != EXPR_VARIABLE && re->expr_type != EXPR_NULL
&& rse->expr != NULL_TREE && !DECL_P (rse->expr))
{
- tmp = gfc_create_var (TREE_TYPE (rse->expr), "rhs");
- pre = &rse->pre;
- gfc_add_modify (&rse->pre, tmp, rse->expr);
+ if (re->ts.type == BT_CLASS && !GFC_CLASS_TYPE_P (TREE_TYPE (rse->expr)))
+ class_expr = gfc_get_class_from_expr (rse->expr);
+
+ if (rse->loop)
+ pre = &rse->loop->pre;
+ else
+ pre = &rse->pre;
+
+ if (class_expr != NULL_TREE && UNLIMITED_POLY (re))
+ {
+ tmp = TREE_OPERAND (rse->expr, 0);
+ tmp = gfc_create_var (TREE_TYPE (tmp), "rhs");
+ gfc_add_modify (&rse->pre, tmp, TREE_OPERAND (rse->expr, 0));
+ }
+ else
+ {
+ tmp = gfc_create_var (TREE_TYPE (rse->expr), "rhs");
+ gfc_add_modify (&rse->pre, tmp, rse->expr);
+ }
+
rse->expr = tmp;
temp_rhs = true;
}
@@ -9001,9 +9165,17 @@ trans_class_vptr_len_assignment (stmtblock_t *block, gfc_expr * le,
else if (temp_rhs && re->ts.type == BT_CLASS)
{
vptr_expr = NULL;
- se.expr = gfc_class_vptr_get (rse->expr);
+ if (class_expr)
+ tmp = class_expr;
+ else if (!GFC_CLASS_TYPE_P (TREE_TYPE (rse->expr)))
+ tmp = gfc_get_class_from_expr (rse->expr);
+ else
+ tmp = rse->expr;
+
+ se.expr = gfc_class_vptr_get (tmp);
if (UNLIMITED_POLY (re))
- from_len = gfc_class_len_get (rse->expr);
+ from_len = gfc_class_len_get (tmp);
+
}
else if (re->expr_type != EXPR_NULL)
/* Only when rhs is non-NULL use its declared type for vptr
@@ -9750,7 +9922,7 @@ gfc_trans_scalar_assign (gfc_se * lse, gfc_se * rse, gfc_typespec ts,
gfc_add_expr_to_block (&block, tmp);
}
}
- else if (gfc_bt_struct (ts.type) || ts.type == BT_CLASS)
+ else if (gfc_bt_struct (ts.type))
{
gfc_add_block_to_block (&block, &lse->pre);
gfc_add_block_to_block (&block, &rse->pre);
@@ -9758,7 +9930,20 @@ gfc_trans_scalar_assign (gfc_se * lse, gfc_se * rse, gfc_typespec ts,
TREE_TYPE (lse->expr), rse->expr);
gfc_add_modify (&block, lse->expr, tmp);
}
- else
+ /* If possible use the rhs vptr copy with trans_scalar_class_assign.... */
+ else if (ts.type == BT_CLASS
+ && !trans_scalar_class_assign (&block, lse, rse))
+ {
+ gfc_add_block_to_block (&block, &lse->pre);
+ gfc_add_block_to_block (&block, &rse->pre);
+ /* ...otherwise assignment suffices. Note the use of VIEW_CONVERT_EXPR
+ for the lhs which ensures that class data rhs cast as a string assigns
+ correctly. */
+ tmp = fold_build1_loc (input_location, VIEW_CONVERT_EXPR,
+ TREE_TYPE (rse->expr), lse->expr);
+ gfc_add_modify (&block, tmp, rse->expr);
+ }
+ else if (ts.type != BT_CLASS)
{
gfc_add_block_to_block (&block, &lse->pre);
gfc_add_block_to_block (&block, &rse->pre);
@@ -10666,23 +10851,53 @@ trans_class_assignment (stmtblock_t *block, gfc_expr *lhs, gfc_expr *rhs,
gfc_se *lse, gfc_se *rse, bool use_vptr_copy,
bool class_realloc)
{
- tree tmp, fcn, stdcopy, to_len, from_len, vptr;
+ tree tmp, fcn, stdcopy, to_len, from_len, vptr, old_vptr;
vec<tree, va_gc> *args = NULL;
+ /* Store the old vptr so that dynamic types can be compared for
+ reallocation to occur or not. */
+ if (class_realloc)
+ {
+ tmp = lse->expr;
+ if (!GFC_CLASS_TYPE_P (TREE_TYPE (tmp)))
+ tmp = gfc_get_class_from_expr (tmp);
+ }
+
vptr = trans_class_vptr_len_assignment (block, lhs, rhs, rse, &to_len,
&from_len);
- /* Generate allocation of the lhs. */
+ /* Generate (re)allocation of the lhs. */
if (class_realloc)
{
- stmtblock_t alloc;
- tree class_han;
+ stmtblock_t alloc, re_alloc;
+ tree class_han, re, size;
- tmp = gfc_vptr_size_get (vptr);
+ if (tmp && GFC_CLASS_TYPE_P (TREE_TYPE (tmp)))
+ old_vptr = gfc_evaluate_now (gfc_class_vptr_get (tmp), block);
+ else
+ old_vptr = build_int_cst (TREE_TYPE (vptr), 0);
+
+ size = gfc_vptr_size_get (vptr);
class_han = GFC_CLASS_TYPE_P (TREE_TYPE (lse->expr))
? gfc_class_data_get (lse->expr) : lse->expr;
+
+ /* Allocate block. */
gfc_init_block (&alloc);
- gfc_allocate_using_malloc (&alloc, class_han, tmp, NULL_TREE);
+ gfc_allocate_using_malloc (&alloc, class_han, size, NULL_TREE);
+
+ /* Reallocate if dynamic types are different. */
+ gfc_init_block (&re_alloc);
+ re = build_call_expr_loc (input_location,
+ builtin_decl_explicit (BUILT_IN_REALLOC), 2,
+ fold_convert (pvoid_type_node, class_han),
+ size);
+ tmp = fold_build2_loc (input_location, NE_EXPR,
+ logical_type_node, vptr, old_vptr);
+ re = fold_build3_loc (input_location, COND_EXPR, void_type_node,
+ tmp, re, build_empty_stmt (input_location));
+ gfc_add_expr_to_block (&re_alloc, re);
+
+ /* Allocate if _data is NULL, reallocate otherwise. */
tmp = fold_build2_loc (input_location, EQ_EXPR,
logical_type_node, class_han,
build_int_cst (prvoid_type_node, 0));
@@ -10690,7 +10905,7 @@ trans_class_assignment (stmtblock_t *block, gfc_expr *lhs, gfc_expr *rhs,
gfc_unlikely (tmp,
PRED_FORTRAN_FAIL_ALLOC),
gfc_finish_block (&alloc),
- build_empty_stmt (input_location));
+ gfc_finish_block (&re_alloc));
gfc_add_expr_to_block (&lse->pre, tmp);
}
@@ -10793,6 +11008,7 @@ gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
bool maybe_workshare = false, lhs_refs_comp = false, rhs_refs_comp = false;
symbol_attribute lhs_caf_attr, rhs_caf_attr, lhs_attr;
bool is_poly_assign;
+ bool realloc_flag;
/* Assignment of the form lhs = rhs. */
gfc_start_block (&block);
@@ -10833,6 +11049,10 @@ gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
|| gfc_is_class_array_ref (expr2, NULL)
|| gfc_is_class_scalar_expr (expr2));
+ realloc_flag = flag_realloc_lhs
+ && gfc_is_reallocatable_lhs (expr1)
+ && expr2->rank
+ && !is_runtime_conformable (expr1, expr2);
/* Only analyze the expressions for coarray properties, when in coarray-lib
mode. */
@@ -11077,8 +11297,9 @@ gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
if (is_poly_assign)
tmp = trans_class_assignment (&body, expr1, expr2, &lse, &rse,
use_vptr_copy || (lhs_attr.allocatable
- && !lhs_attr.dimension),
- flag_realloc_lhs && !lhs_attr.pointer);
+ && !lhs_attr.dimension),
+ !realloc_flag && flag_realloc_lhs
+ && !lhs_attr.pointer);
else if (flag_coarray == GFC_FCOARRAY_LIB
&& lhs_caf_attr.codimension && rhs_caf_attr.codimension
&& ((lhs_caf_attr.allocatable && lhs_refs_comp)
@@ -11108,7 +11329,8 @@ gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
{
/* This case comes about when the scalarizer provides array element
references. Use the vptr copy function, since this does a deep
- copy of allocatable components, without which the finalizer call */
+ copy of allocatable components, without which the finalizer call
+ will deallocate the components. */
tmp = gfc_get_vptr_from_expr (rse.expr);
if (tmp != NULL_TREE)
{
@@ -11183,10 +11405,7 @@ gfc_trans_assignment_1 (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
}
/* F2003: Allocate or reallocate lhs of allocatable array. */
- if (flag_realloc_lhs
- && gfc_is_reallocatable_lhs (expr1)
- && expr2->rank
- && !is_runtime_conformable (expr1, expr2))
+ if (realloc_flag)
{
realloc_lhs_warning (expr1->ts.type, true, &expr1->where);
ompws_flags &= ~OMPWS_SCALARIZER_WS;
@@ -11295,8 +11514,7 @@ gfc_trans_assignment (gfc_expr * expr1, gfc_expr * expr2, bool init_flag,
return tmp;
}
- if (UNLIMITED_POLY (expr1) && expr1->rank
- && expr2->ts.type != BT_CLASS)
+ if (UNLIMITED_POLY (expr1) && expr1->rank)
use_vptr_copy = true;
/* Fallback to the scalarizer to generate explicit loops. */