blob: 6552eaf3b0ca5c7f74bb5d7060b2a79dd119a6c9 [file] [log] [blame]
/* Array things
Copyright (C) 2000-2021 Free Software Foundation, Inc.
Contributed by Andy Vaught
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "options.h"
#include "gfortran.h"
#include "parse.h"
#include "match.h"
#include "constructor.h"
/**************** Array reference matching subroutines *****************/
/* Copy an array reference structure. */
gfc_array_ref *
gfc_copy_array_ref (gfc_array_ref *src)
{
gfc_array_ref *dest;
int i;
if (src == NULL)
return NULL;
dest = gfc_get_array_ref ();
*dest = *src;
for (i = 0; i < GFC_MAX_DIMENSIONS; i++)
{
dest->start[i] = gfc_copy_expr (src->start[i]);
dest->end[i] = gfc_copy_expr (src->end[i]);
dest->stride[i] = gfc_copy_expr (src->stride[i]);
}
return dest;
}
/* Match a single dimension of an array reference. This can be a
single element or an array section. Any modifications we've made
to the ar structure are cleaned up by the caller. If the init
is set, we require the subscript to be a valid initialization
expression. */
static match
match_subscript (gfc_array_ref *ar, int init, bool match_star)
{
match m = MATCH_ERROR;
bool star = false;
int i;
bool saw_boz = false;
i = ar->dimen + ar->codimen;
gfc_gobble_whitespace ();
ar->c_where[i] = gfc_current_locus;
ar->start[i] = ar->end[i] = ar->stride[i] = NULL;
/* We can't be sure of the difference between DIMEN_ELEMENT and
DIMEN_VECTOR until we know the type of the element itself at
resolution time. */
ar->dimen_type[i] = DIMEN_UNKNOWN;
if (gfc_match_char (':') == MATCH_YES)
goto end_element;
/* Get start element. */
if (match_star && (m = gfc_match_char ('*')) == MATCH_YES)
star = true;
if (!star && init)
m = gfc_match_init_expr (&ar->start[i]);
else if (!star)
m = gfc_match_expr (&ar->start[i]);
if (ar->start[i] && ar->start[i]->ts.type == BT_BOZ)
{
gfc_error ("Invalid BOZ literal constant used in subscript at %C");
saw_boz = true;
}
if (m == MATCH_NO)
gfc_error ("Expected array subscript at %C");
if (m != MATCH_YES)
return MATCH_ERROR;
if (gfc_match_char (':') == MATCH_NO)
goto matched;
if (star)
{
gfc_error ("Unexpected %<*%> in coarray subscript at %C");
return MATCH_ERROR;
}
/* Get an optional end element. Because we've seen the colon, we
definitely have a range along this dimension. */
end_element:
ar->dimen_type[i] = DIMEN_RANGE;
if (match_star && (m = gfc_match_char ('*')) == MATCH_YES)
star = true;
else if (init)
m = gfc_match_init_expr (&ar->end[i]);
else
m = gfc_match_expr (&ar->end[i]);
if (ar->end[i] && ar->end[i]->ts.type == BT_BOZ)
{
gfc_error ("Invalid BOZ literal constant used in subscript at %C");
saw_boz = true;
}
if (m == MATCH_ERROR)
return MATCH_ERROR;
/* See if we have an optional stride. */
if (gfc_match_char (':') == MATCH_YES)
{
if (star)
{
gfc_error ("Strides not allowed in coarray subscript at %C");
return MATCH_ERROR;
}
m = init ? gfc_match_init_expr (&ar->stride[i])
: gfc_match_expr (&ar->stride[i]);
if (ar->stride[i] && ar->stride[i]->ts.type == BT_BOZ)
{
gfc_error ("Invalid BOZ literal constant used in subscript at %C");
saw_boz = true;
}
if (m == MATCH_NO)
gfc_error ("Expected array subscript stride at %C");
if (m != MATCH_YES)
return MATCH_ERROR;
}
matched:
if (star)
ar->dimen_type[i] = DIMEN_STAR;
return (saw_boz ? MATCH_ERROR : MATCH_YES);
}
/* Match an array reference, whether it is the whole array or particular
elements or a section. If init is set, the reference has to consist
of init expressions. */
match
gfc_match_array_ref (gfc_array_ref *ar, gfc_array_spec *as, int init,
int corank)
{
match m;
bool matched_bracket = false;
gfc_expr *tmp;
bool stat_just_seen = false;
bool team_just_seen = false;
memset (ar, '\0', sizeof (*ar));
ar->where = gfc_current_locus;
ar->as = as;
ar->type = AR_UNKNOWN;
if (gfc_match_char ('[') == MATCH_YES)
{
matched_bracket = true;
goto coarray;
}
if (gfc_match_char ('(') != MATCH_YES)
{
ar->type = AR_FULL;
ar->dimen = 0;
return MATCH_YES;
}
for (ar->dimen = 0; ar->dimen < GFC_MAX_DIMENSIONS; ar->dimen++)
{
m = match_subscript (ar, init, false);
if (m == MATCH_ERROR)
return MATCH_ERROR;
if (gfc_match_char (')') == MATCH_YES)
{
ar->dimen++;
goto coarray;
}
if (gfc_match_char (',') != MATCH_YES)
{
gfc_error ("Invalid form of array reference at %C");
return MATCH_ERROR;
}
}
if (ar->dimen >= 7
&& !gfc_notify_std (GFC_STD_F2008,
"Array reference at %C has more than 7 dimensions"))
return MATCH_ERROR;
gfc_error ("Array reference at %C cannot have more than %d dimensions",
GFC_MAX_DIMENSIONS);
return MATCH_ERROR;
coarray:
if (!matched_bracket && gfc_match_char ('[') != MATCH_YES)
{
if (ar->dimen > 0)
return MATCH_YES;
else
return MATCH_ERROR;
}
if (flag_coarray == GFC_FCOARRAY_NONE)
{
gfc_fatal_error ("Coarrays disabled at %C, use %<-fcoarray=%> to enable");
return MATCH_ERROR;
}
if (corank == 0)
{
gfc_error ("Unexpected coarray designator at %C");
return MATCH_ERROR;
}
ar->stat = NULL;
for (ar->codimen = 0; ar->codimen + ar->dimen < GFC_MAX_DIMENSIONS; ar->codimen++)
{
m = match_subscript (ar, init, true);
if (m == MATCH_ERROR)
return MATCH_ERROR;
team_just_seen = false;
stat_just_seen = false;
if (gfc_match (" , team = %e", &tmp) == MATCH_YES && ar->team == NULL)
{
ar->team = tmp;
team_just_seen = true;
}
if (ar->team && !team_just_seen)
{
gfc_error ("TEAM= attribute in %C misplaced");
return MATCH_ERROR;
}
if (gfc_match (" , stat = %e",&tmp) == MATCH_YES && ar->stat == NULL)
{
ar->stat = tmp;
stat_just_seen = true;
}
if (ar->stat && !stat_just_seen)
{
gfc_error ("STAT= attribute in %C misplaced");
return MATCH_ERROR;
}
if (gfc_match_char (']') == MATCH_YES)
{
ar->codimen++;
if (ar->codimen < corank)
{
gfc_error ("Too few codimensions at %C, expected %d not %d",
corank, ar->codimen);
return MATCH_ERROR;
}
if (ar->codimen > corank)
{
gfc_error ("Too many codimensions at %C, expected %d not %d",
corank, ar->codimen);
return MATCH_ERROR;
}
return MATCH_YES;
}
if (gfc_match_char (',') != MATCH_YES)
{
if (gfc_match_char ('*') == MATCH_YES)
gfc_error ("Unexpected %<*%> for codimension %d of %d at %C",
ar->codimen + 1, corank);
else
gfc_error ("Invalid form of coarray reference at %C");
return MATCH_ERROR;
}
else if (ar->dimen_type[ar->codimen + ar->dimen] == DIMEN_STAR)
{
gfc_error ("Unexpected %<*%> for codimension %d of %d at %C",
ar->codimen + 1, corank);
return MATCH_ERROR;
}
if (ar->codimen >= corank)
{
gfc_error ("Invalid codimension %d at %C, only %d codimensions exist",
ar->codimen + 1, corank);
return MATCH_ERROR;
}
}
gfc_error ("Array reference at %C cannot have more than %d dimensions",
GFC_MAX_DIMENSIONS);
return MATCH_ERROR;
}
/************** Array specification matching subroutines ***************/
/* Free all of the expressions associated with array bounds
specifications. */
void
gfc_free_array_spec (gfc_array_spec *as)
{
int i;
if (as == NULL)
return;
if (as->corank == 0)
{
for (i = 0; i < as->rank; i++)
{
gfc_free_expr (as->lower[i]);
gfc_free_expr (as->upper[i]);
}
}
else
{
int n = as->rank + as->corank - (as->cotype == AS_EXPLICIT ? 1 : 0);
for (i = 0; i < n; i++)
{
gfc_free_expr (as->lower[i]);
gfc_free_expr (as->upper[i]);
}
}
free (as);
}
/* Take an array bound, resolves the expression, that make up the
shape and check associated constraints. */
static bool
resolve_array_bound (gfc_expr *e, int check_constant)
{
if (e == NULL)
return true;
if (!gfc_resolve_expr (e)
|| !gfc_specification_expr (e))
return false;
if (check_constant && !gfc_is_constant_expr (e))
{
if (e->expr_type == EXPR_VARIABLE)
gfc_error ("Variable %qs at %L in this context must be constant",
e->symtree->n.sym->name, &e->where);
else
gfc_error ("Expression at %L in this context must be constant",
&e->where);
return false;
}
return true;
}
/* Takes an array specification, resolves the expressions that make up
the shape and make sure everything is integral. */
bool
gfc_resolve_array_spec (gfc_array_spec *as, int check_constant)
{
gfc_expr *e;
int i;
if (as == NULL)
return true;
if (as->resolved)
return true;
for (i = 0; i < as->rank + as->corank; i++)
{
if (i == GFC_MAX_DIMENSIONS)
return false;
e = as->lower[i];
if (!resolve_array_bound (e, check_constant))
return false;
e = as->upper[i];
if (!resolve_array_bound (e, check_constant))
return false;
if ((as->lower[i] == NULL) || (as->upper[i] == NULL))
continue;
/* If the size is negative in this dimension, set it to zero. */
if (as->lower[i]->expr_type == EXPR_CONSTANT
&& as->upper[i]->expr_type == EXPR_CONSTANT
&& mpz_cmp (as->upper[i]->value.integer,
as->lower[i]->value.integer) < 0)
{
gfc_free_expr (as->upper[i]);
as->upper[i] = gfc_copy_expr (as->lower[i]);
mpz_sub_ui (as->upper[i]->value.integer,
as->upper[i]->value.integer, 1);
}
}
as->resolved = true;
return true;
}
/* Match a single array element specification. The return values as
well as the upper and lower bounds of the array spec are filled
in according to what we see on the input. The caller makes sure
individual specifications make sense as a whole.
Parsed Lower Upper Returned
------------------------------------
: NULL NULL AS_DEFERRED (*)
x 1 x AS_EXPLICIT
x: x NULL AS_ASSUMED_SHAPE
x:y x y AS_EXPLICIT
x:* x NULL AS_ASSUMED_SIZE
* 1 NULL AS_ASSUMED_SIZE
(*) For non-pointer dummy arrays this is AS_ASSUMED_SHAPE. This
is fixed during the resolution of formal interfaces.
Anything else AS_UNKNOWN. */
static array_type
match_array_element_spec (gfc_array_spec *as)
{
gfc_expr **upper, **lower;
match m;
int rank;
rank = as->rank == -1 ? 0 : as->rank;
lower = &as->lower[rank + as->corank - 1];
upper = &as->upper[rank + as->corank - 1];
if (gfc_match_char ('*') == MATCH_YES)
{
*lower = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1);
return AS_ASSUMED_SIZE;
}
if (gfc_match_char (':') == MATCH_YES)
return AS_DEFERRED;
m = gfc_match_expr (upper);
if (m == MATCH_NO)
gfc_error ("Expected expression in array specification at %C");
if (m != MATCH_YES)
return AS_UNKNOWN;
if (!gfc_expr_check_typed (*upper, gfc_current_ns, false))
return AS_UNKNOWN;
if (((*upper)->expr_type == EXPR_CONSTANT
&& (*upper)->ts.type != BT_INTEGER) ||
((*upper)->expr_type == EXPR_FUNCTION
&& (*upper)->ts.type == BT_UNKNOWN
&& (*upper)->symtree
&& strcmp ((*upper)->symtree->name, "null") == 0))
{
gfc_error ("Expecting a scalar INTEGER expression at %C, found %s",
gfc_basic_typename ((*upper)->ts.type));
return AS_UNKNOWN;
}
if (gfc_match_char (':') == MATCH_NO)
{
*lower = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1);
return AS_EXPLICIT;
}
*lower = *upper;
*upper = NULL;
if (gfc_match_char ('*') == MATCH_YES)
return AS_ASSUMED_SIZE;
m = gfc_match_expr (upper);
if (m == MATCH_ERROR)
return AS_UNKNOWN;
if (m == MATCH_NO)
return AS_ASSUMED_SHAPE;
if (!gfc_expr_check_typed (*upper, gfc_current_ns, false))
return AS_UNKNOWN;
if (((*upper)->expr_type == EXPR_CONSTANT
&& (*upper)->ts.type != BT_INTEGER) ||
((*upper)->expr_type == EXPR_FUNCTION
&& (*upper)->ts.type == BT_UNKNOWN
&& (*upper)->symtree
&& strcmp ((*upper)->symtree->name, "null") == 0))
{
gfc_error ("Expecting a scalar INTEGER expression at %C, found %s",
gfc_basic_typename ((*upper)->ts.type));
return AS_UNKNOWN;
}
return AS_EXPLICIT;
}
/* Matches an array specification, incidentally figuring out what sort
it is. Match either a normal array specification, or a coarray spec
or both. Optionally allow [:] for coarrays. */
match
gfc_match_array_spec (gfc_array_spec **asp, bool match_dim, bool match_codim)
{
array_type current_type;
gfc_array_spec *as;
int i;
as = gfc_get_array_spec ();
if (!match_dim)
goto coarray;
if (gfc_match_char ('(') != MATCH_YES)
{
if (!match_codim)
goto done;
goto coarray;
}
if (gfc_match (" .. )") == MATCH_YES)
{
as->type = AS_ASSUMED_RANK;
as->rank = -1;
if (!gfc_notify_std (GFC_STD_F2018, "Assumed-rank array at %C"))
goto cleanup;
if (!match_codim)
goto done;
goto coarray;
}
for (;;)
{
as->rank++;
current_type = match_array_element_spec (as);
/* Note that current_type == AS_ASSUMED_SIZE for both assumed-size
and implied-shape specifications. If the rank is at least 2, we can
distinguish between them. But for rank 1, we currently return
ASSUMED_SIZE; this gets adjusted later when we know for sure
whether the symbol parsed is a PARAMETER or not. */
if (as->rank == 1)
{
if (current_type == AS_UNKNOWN)
goto cleanup;
as->type = current_type;
}
else
switch (as->type)
{ /* See how current spec meshes with the existing. */
case AS_UNKNOWN:
goto cleanup;
case AS_IMPLIED_SHAPE:
if (current_type != AS_ASSUMED_SIZE)
{
gfc_error ("Bad array specification for implied-shape"
" array at %C");
goto cleanup;
}
break;
case AS_EXPLICIT:
if (current_type == AS_ASSUMED_SIZE)
{
as->type = AS_ASSUMED_SIZE;
break;
}
if (current_type == AS_EXPLICIT)
break;
gfc_error ("Bad array specification for an explicitly shaped "
"array at %C");
goto cleanup;
case AS_ASSUMED_SHAPE:
if ((current_type == AS_ASSUMED_SHAPE)
|| (current_type == AS_DEFERRED))
break;
gfc_error ("Bad array specification for assumed shape "
"array at %C");
goto cleanup;
case AS_DEFERRED:
if (current_type == AS_DEFERRED)
break;
if (current_type == AS_ASSUMED_SHAPE)
{
as->type = AS_ASSUMED_SHAPE;
break;
}
gfc_error ("Bad specification for deferred shape array at %C");
goto cleanup;
case AS_ASSUMED_SIZE:
if (as->rank == 2 && current_type == AS_ASSUMED_SIZE)
{
as->type = AS_IMPLIED_SHAPE;
break;
}
gfc_error ("Bad specification for assumed size array at %C");
goto cleanup;
case AS_ASSUMED_RANK:
gcc_unreachable ();
}
if (gfc_match_char (')') == MATCH_YES)
break;
if (gfc_match_char (',') != MATCH_YES)
{
gfc_error ("Expected another dimension in array declaration at %C");
goto cleanup;
}
if (as->rank + as->corank >= GFC_MAX_DIMENSIONS)
{
gfc_error ("Array specification at %C has more than %d dimensions",
GFC_MAX_DIMENSIONS);
goto cleanup;
}
if (as->corank + as->rank >= 7
&& !gfc_notify_std (GFC_STD_F2008, "Array specification at %C "
"with more than 7 dimensions"))
goto cleanup;
}
if (!match_codim)
goto done;
coarray:
if (gfc_match_char ('[') != MATCH_YES)
goto done;
if (!gfc_notify_std (GFC_STD_F2008, "Coarray declaration at %C"))
goto cleanup;
if (flag_coarray == GFC_FCOARRAY_NONE)
{
gfc_fatal_error ("Coarrays disabled at %C, use %<-fcoarray=%> to enable");
goto cleanup;
}
if (as->rank >= GFC_MAX_DIMENSIONS)
{
gfc_error ("Array specification at %C has more than %d "
"dimensions", GFC_MAX_DIMENSIONS);
goto cleanup;
}
for (;;)
{
as->corank++;
current_type = match_array_element_spec (as);
if (current_type == AS_UNKNOWN)
goto cleanup;
if (as->corank == 1)
as->cotype = current_type;
else
switch (as->cotype)
{ /* See how current spec meshes with the existing. */
case AS_IMPLIED_SHAPE:
case AS_UNKNOWN:
goto cleanup;
case AS_EXPLICIT:
if (current_type == AS_ASSUMED_SIZE)
{
as->cotype = AS_ASSUMED_SIZE;
break;
}
if (current_type == AS_EXPLICIT)
break;
gfc_error ("Bad array specification for an explicitly "
"shaped array at %C");
goto cleanup;
case AS_ASSUMED_SHAPE:
if ((current_type == AS_ASSUMED_SHAPE)
|| (current_type == AS_DEFERRED))
break;
gfc_error ("Bad array specification for assumed shape "
"array at %C");
goto cleanup;
case AS_DEFERRED:
if (current_type == AS_DEFERRED)
break;
if (current_type == AS_ASSUMED_SHAPE)
{
as->cotype = AS_ASSUMED_SHAPE;
break;
}
gfc_error ("Bad specification for deferred shape array at %C");
goto cleanup;
case AS_ASSUMED_SIZE:
gfc_error ("Bad specification for assumed size array at %C");
goto cleanup;
case AS_ASSUMED_RANK:
gcc_unreachable ();
}
if (gfc_match_char (']') == MATCH_YES)
break;
if (gfc_match_char (',') != MATCH_YES)
{
gfc_error ("Expected another dimension in array declaration at %C");
goto cleanup;
}
if (as->rank + as->corank >= GFC_MAX_DIMENSIONS)
{
gfc_error ("Array specification at %C has more than %d "
"dimensions", GFC_MAX_DIMENSIONS);
goto cleanup;
}
}
if (current_type == AS_EXPLICIT)
{
gfc_error ("Upper bound of last coarray dimension must be %<*%> at %C");
goto cleanup;
}
if (as->cotype == AS_ASSUMED_SIZE)
as->cotype = AS_EXPLICIT;
if (as->rank == 0)
as->type = as->cotype;
done:
if (as->rank == 0 && as->corank == 0)
{
*asp = NULL;
gfc_free_array_spec (as);
return MATCH_NO;
}
/* If a lower bounds of an assumed shape array is blank, put in one. */
if (as->type == AS_ASSUMED_SHAPE)
{
for (i = 0; i < as->rank + as->corank; i++)
{
if (as->lower[i] == NULL)
as->lower[i] = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1);
}
}
*asp = as;
return MATCH_YES;
cleanup:
/* Something went wrong. */
gfc_free_array_spec (as);
return MATCH_ERROR;
}
/* Given a symbol and an array specification, modify the symbol to
have that array specification. The error locus is needed in case
something goes wrong. On failure, the caller must free the spec. */
bool
gfc_set_array_spec (gfc_symbol *sym, gfc_array_spec *as, locus *error_loc)
{
int i;
symbol_attribute *attr;
if (as == NULL)
return true;
/* If the symbol corresponds to a submodule module procedure the array spec is
already set, so do not attempt to set it again here. */
attr = &sym->attr;
if (gfc_submodule_procedure(attr))
return true;
if (as->rank
&& !gfc_add_dimension (&sym->attr, sym->name, error_loc))
return false;
if (as->corank
&& !gfc_add_codimension (&sym->attr, sym->name, error_loc))
return false;
if (sym->as == NULL)
{
sym->as = as;
return true;
}
if ((sym->as->type == AS_ASSUMED_RANK && as->corank)
|| (as->type == AS_ASSUMED_RANK && sym->as->corank))
{
gfc_error ("The assumed-rank array %qs at %L shall not have a "
"codimension", sym->name, error_loc);
return false;
}
/* Check F2018:C822. */
if (sym->as->rank + sym->as->corank > GFC_MAX_DIMENSIONS)
goto too_many;
if (as->corank)
{
sym->as->cotype = as->cotype;
sym->as->corank = as->corank;
/* Check F2018:C822. */
if (sym->as->rank + sym->as->corank > GFC_MAX_DIMENSIONS)
goto too_many;
for (i = 0; i < as->corank; i++)
{
sym->as->lower[sym->as->rank + i] = as->lower[i];
sym->as->upper[sym->as->rank + i] = as->upper[i];
}
}
else
{
/* The "sym" has no rank (checked via gfc_add_dimension). Thus
the dimension is added - but first the codimensions (if existing
need to be shifted to make space for the dimension. */
gcc_assert (as->corank == 0 && sym->as->rank == 0);
sym->as->rank = as->rank;
sym->as->type = as->type;
sym->as->cray_pointee = as->cray_pointee;
sym->as->cp_was_assumed = as->cp_was_assumed;
/* Check F2018:C822. */
if (sym->as->rank + sym->as->corank > GFC_MAX_DIMENSIONS)
goto too_many;
for (i = sym->as->corank - 1; i >= 0; i--)
{
sym->as->lower[as->rank + i] = sym->as->lower[i];
sym->as->upper[as->rank + i] = sym->as->upper[i];
}
for (i = 0; i < as->rank; i++)
{
sym->as->lower[i] = as->lower[i];
sym->as->upper[i] = as->upper[i];
}
}
free (as);
return true;
too_many:
gfc_error ("rank + corank of %qs exceeds %d at %C", sym->name,
GFC_MAX_DIMENSIONS);
return false;
}
/* Copy an array specification. */
gfc_array_spec *
gfc_copy_array_spec (gfc_array_spec *src)
{
gfc_array_spec *dest;
int i;
if (src == NULL)
return NULL;
dest = gfc_get_array_spec ();
*dest = *src;
for (i = 0; i < dest->rank + dest->corank; i++)
{
dest->lower[i] = gfc_copy_expr (dest->lower[i]);
dest->upper[i] = gfc_copy_expr (dest->upper[i]);
}
return dest;
}
/* Returns nonzero if the two expressions are equal. Only handles integer
constants. */
static int
compare_bounds (gfc_expr *bound1, gfc_expr *bound2)
{
if (bound1 == NULL || bound2 == NULL
|| bound1->expr_type != EXPR_CONSTANT
|| bound2->expr_type != EXPR_CONSTANT
|| bound1->ts.type != BT_INTEGER
|| bound2->ts.type != BT_INTEGER)
gfc_internal_error ("gfc_compare_array_spec(): Array spec clobbered");
if (mpz_cmp (bound1->value.integer, bound2->value.integer) == 0)
return 1;
else
return 0;
}
/* Compares two array specifications. They must be constant or deferred
shape. */
int
gfc_compare_array_spec (gfc_array_spec *as1, gfc_array_spec *as2)
{
int i;
if (as1 == NULL && as2 == NULL)
return 1;
if (as1 == NULL || as2 == NULL)
return 0;
if (as1->rank != as2->rank)
return 0;
if (as1->corank != as2->corank)
return 0;
if (as1->rank == 0)
return 1;
if (as1->type != as2->type)
return 0;
if (as1->type == AS_EXPLICIT)
for (i = 0; i < as1->rank + as1->corank; i++)
{
if (compare_bounds (as1->lower[i], as2->lower[i]) == 0)
return 0;
if (compare_bounds (as1->upper[i], as2->upper[i]) == 0)
return 0;
}
return 1;
}
/****************** Array constructor functions ******************/
/* Given an expression node that might be an array constructor and a
symbol, make sure that no iterators in this or child constructors
use the symbol as an implied-DO iterator. Returns nonzero if a
duplicate was found. */
static int
check_duplicate_iterator (gfc_constructor_base base, gfc_symbol *master)
{
gfc_constructor *c;
gfc_expr *e;
for (c = gfc_constructor_first (base); c; c = gfc_constructor_next (c))
{
e = c->expr;
if (e->expr_type == EXPR_ARRAY
&& check_duplicate_iterator (e->value.constructor, master))
return 1;
if (c->iterator == NULL)
continue;
if (c->iterator->var->symtree->n.sym == master)
{
gfc_error ("DO-iterator %qs at %L is inside iterator of the "
"same name", master->name, &c->where);
return 1;
}
}
return 0;
}
/* Forward declaration because these functions are mutually recursive. */
static match match_array_cons_element (gfc_constructor_base *);
/* Match a list of array elements. */
static match
match_array_list (gfc_constructor_base *result)
{
gfc_constructor_base head;
gfc_constructor *p;
gfc_iterator iter;
locus old_loc;
gfc_expr *e;
match m;
int n;
old_loc = gfc_current_locus;
if (gfc_match_char ('(') == MATCH_NO)
return MATCH_NO;
memset (&iter, '\0', sizeof (gfc_iterator));
head = NULL;
m = match_array_cons_element (&head);
if (m != MATCH_YES)
goto cleanup;
if (gfc_match_char (',') != MATCH_YES)
{
m = MATCH_NO;
goto cleanup;
}
for (n = 1;; n++)
{
m = gfc_match_iterator (&iter, 0);
if (m == MATCH_YES)
break;
if (m == MATCH_ERROR)
goto cleanup;
m = match_array_cons_element (&head);
if (m == MATCH_ERROR)
goto cleanup;
if (m == MATCH_NO)
{
if (n > 2)
goto syntax;
m = MATCH_NO;
goto cleanup; /* Could be a complex constant */
}
if (gfc_match_char (',') != MATCH_YES)
{
if (n > 2)
goto syntax;
m = MATCH_NO;
goto cleanup;
}
}
if (gfc_match_char (')') != MATCH_YES)
goto syntax;
if (check_duplicate_iterator (head, iter.var->symtree->n.sym))
{
m = MATCH_ERROR;
goto cleanup;
}
e = gfc_get_array_expr (BT_UNKNOWN, 0, &old_loc);
e->value.constructor = head;
p = gfc_constructor_append_expr (result, e, &gfc_current_locus);
p->iterator = gfc_get_iterator ();
*p->iterator = iter;
return MATCH_YES;
syntax:
gfc_error ("Syntax error in array constructor at %C");
m = MATCH_ERROR;
cleanup:
gfc_constructor_free (head);
gfc_free_iterator (&iter, 0);
gfc_current_locus = old_loc;
return m;
}
/* Match a single element of an array constructor, which can be a
single expression or a list of elements. */
static match
match_array_cons_element (gfc_constructor_base *result)
{
gfc_expr *expr;
match m;
m = match_array_list (result);
if (m != MATCH_NO)
return m;
m = gfc_match_expr (&expr);
if (m != MATCH_YES)
return m;
if (expr->ts.type == BT_BOZ)
{
gfc_error ("BOZ literal constant at %L cannot appear in an "
"array constructor", &expr->where);
goto done;
}
if (expr->expr_type == EXPR_FUNCTION
&& expr->ts.type == BT_UNKNOWN
&& strcmp(expr->symtree->name, "null") == 0)
{
gfc_error ("NULL() at %C cannot appear in an array constructor");
goto done;
}
gfc_constructor_append_expr (result, expr, &gfc_current_locus);
return MATCH_YES;
done:
gfc_free_expr (expr);
return MATCH_ERROR;
}
/* Convert components of an array constructor to the type in ts. */
static match
walk_array_constructor (gfc_typespec *ts, gfc_constructor_base head)
{
gfc_constructor *c;
gfc_expr *e;
match m;
for (c = gfc_constructor_first (head); c; c = gfc_constructor_next (c))
{
e = c->expr;
if (e->expr_type == EXPR_ARRAY && e->ts.type == BT_UNKNOWN
&& !e->ref && e->value.constructor)
{
m = walk_array_constructor (ts, e->value.constructor);
if (m == MATCH_ERROR)
return m;
}
else if (!gfc_convert_type_warn (e, ts, 1, 1, true)
&& e->ts.type != BT_UNKNOWN)
return MATCH_ERROR;
}
return MATCH_YES;
}
/* Match an array constructor. */
match
gfc_match_array_constructor (gfc_expr **result)
{
gfc_constructor *c;
gfc_constructor_base head;
gfc_expr *expr;
gfc_typespec ts;
locus where;
match m;
const char *end_delim;
bool seen_ts;
head = NULL;
seen_ts = false;
if (gfc_match (" (/") == MATCH_NO)
{
if (gfc_match (" [") == MATCH_NO)
return MATCH_NO;
else
{
if (!gfc_notify_std (GFC_STD_F2003, "[...] "
"style array constructors at %C"))
return MATCH_ERROR;
end_delim = " ]";
}
}
else
end_delim = " /)";
where = gfc_current_locus;
/* Try to match an optional "type-spec ::" */
gfc_clear_ts (&ts);
m = gfc_match_type_spec (&ts);
if (m == MATCH_YES)
{
seen_ts = (gfc_match (" ::") == MATCH_YES);
if (seen_ts)
{
if (!gfc_notify_std (GFC_STD_F2003, "Array constructor "
"including type specification at %C"))
goto cleanup;
if (ts.deferred)
{
gfc_error ("Type-spec at %L cannot contain a deferred "
"type parameter", &where);
goto cleanup;
}
if (ts.type == BT_CHARACTER
&& ts.u.cl && !ts.u.cl->length && !ts.u.cl->length_from_typespec)
{
gfc_error ("Type-spec at %L cannot contain an asterisk for a "
"type parameter", &where);
goto cleanup;
}
}
}
else if (m == MATCH_ERROR)
goto cleanup;
if (!seen_ts)
gfc_current_locus = where;
if (gfc_match (end_delim) == MATCH_YES)
{
if (seen_ts)
goto done;
else
{
gfc_error ("Empty array constructor at %C is not allowed");
goto cleanup;
}
}
for (;;)
{
m = match_array_cons_element (&head);
if (m == MATCH_ERROR)
goto cleanup;
if (m == MATCH_NO)
goto syntax;
if (gfc_match_char (',') == MATCH_NO)
break;
}
if (gfc_match (end_delim) == MATCH_NO)
goto syntax;
done:
/* Size must be calculated at resolution time. */
if (seen_ts)
{
expr = gfc_get_array_expr (ts.type, ts.kind, &where);
expr->ts = ts;
/* If the typespec is CHARACTER, check that array elements can
be converted. See PR fortran/67803. */
if (ts.type == BT_CHARACTER)
{
c = gfc_constructor_first (head);
for (; c; c = gfc_constructor_next (c))
{
if (gfc_numeric_ts (&c->expr->ts)
|| c->expr->ts.type == BT_LOGICAL)
{
gfc_error ("Incompatible typespec for array element at %L",
&c->expr->where);
return MATCH_ERROR;
}
/* Special case null(). */
if (c->expr->expr_type == EXPR_FUNCTION
&& c->expr->ts.type == BT_UNKNOWN
&& strcmp (c->expr->symtree->name, "null") == 0)
{
gfc_error ("Incompatible typespec for array element at %L",
&c->expr->where);
return MATCH_ERROR;
}
}
}
/* Walk the constructor, and if possible, do type conversion for
numeric types. */
if (gfc_numeric_ts (&ts))
{
m = walk_array_constructor (&ts, head);
if (m == MATCH_ERROR)
return m;
}
}
else
expr = gfc_get_array_expr (BT_UNKNOWN, 0, &where);
expr->value.constructor = head;
if (expr->ts.u.cl)
expr->ts.u.cl->length_from_typespec = seen_ts;
*result = expr;
return MATCH_YES;
syntax:
gfc_error ("Syntax error in array constructor at %C");
cleanup:
gfc_constructor_free (head);
return MATCH_ERROR;
}
/************** Check array constructors for correctness **************/
/* Given an expression, compare it's type with the type of the current
constructor. Returns nonzero if an error was issued. The
cons_state variable keeps track of whether the type of the
constructor being read or resolved is known to be good, bad or just
starting out. */
static gfc_typespec constructor_ts;
static enum
{ CONS_START, CONS_GOOD, CONS_BAD }
cons_state;
static int
check_element_type (gfc_expr *expr, bool convert)
{
if (cons_state == CONS_BAD)
return 0; /* Suppress further errors */
if (cons_state == CONS_START)
{
if (expr->ts.type == BT_UNKNOWN)
cons_state = CONS_BAD;
else
{
cons_state = CONS_GOOD;
constructor_ts = expr->ts;
}
return 0;
}
if (gfc_compare_types (&constructor_ts, &expr->ts))
return 0;
if (convert)
return gfc_convert_type_warn (expr, &constructor_ts, 1, 1, true) ? 0 : 1;
gfc_error ("Element in %s array constructor at %L is %s",
gfc_typename (&constructor_ts), &expr->where,
gfc_typename (expr));
cons_state = CONS_BAD;
return 1;
}
/* Recursive work function for gfc_check_constructor_type(). */
static bool
check_constructor_type (gfc_constructor_base base, bool convert)
{
gfc_constructor *c;
gfc_expr *e;
for (c = gfc_constructor_first (base); c; c = gfc_constructor_next (c))
{
e = c->expr;
if (e->expr_type == EXPR_ARRAY)
{
if (!check_constructor_type (e->value.constructor, convert))
return false;
continue;
}
if (check_element_type (e, convert))
return false;
}
return true;
}
/* Check that all elements of an array constructor are the same type.
On false, an error has been generated. */
bool
gfc_check_constructor_type (gfc_expr *e)
{
bool t;
if (e->ts.type != BT_UNKNOWN)
{
cons_state = CONS_GOOD;
constructor_ts = e->ts;
}
else
{
cons_state = CONS_START;
gfc_clear_ts (&constructor_ts);
}
/* If e->ts.type != BT_UNKNOWN, the array constructor included a
typespec, and we will now convert the values on the fly. */
t = check_constructor_type (e->value.constructor, e->ts.type != BT_UNKNOWN);
if (t && e->ts.type == BT_UNKNOWN)
e->ts = constructor_ts;
return t;
}
typedef struct cons_stack
{
gfc_iterator *iterator;
struct cons_stack *previous;
}
cons_stack;
static cons_stack *base;
static bool check_constructor (gfc_constructor_base, bool (*) (gfc_expr *));
/* Check an EXPR_VARIABLE expression in a constructor to make sure
that that variable is an iteration variable. */
bool
gfc_check_iter_variable (gfc_expr *expr)
{
gfc_symbol *sym;
cons_stack *c;
sym = expr->symtree->n.sym;
for (c = base; c && c->iterator; c = c->previous)
if (sym == c->iterator->var->symtree->n.sym)
return true;
return false;
}
/* Recursive work function for gfc_check_constructor(). This amounts
to calling the check function for each expression in the
constructor, giving variables with the names of iterators a pass. */
static bool
check_constructor (gfc_constructor_base ctor, bool (*check_function) (gfc_expr *))
{
cons_stack element;
gfc_expr *e;
bool t;
gfc_constructor *c;
for (c = gfc_constructor_first (ctor); c; c = gfc_constructor_next (c))
{
e = c->expr;
if (!e)
continue;
if (e->expr_type != EXPR_ARRAY)
{
if (!(*check_function)(e))
return false;
continue;
}
element.previous = base;
element.iterator = c->iterator;
base = &element;
t = check_constructor (e->value.constructor, check_function);
base = element.previous;
if (!t)
return false;
}
/* Nothing went wrong, so all OK. */
return true;
}
/* Checks a constructor to see if it is a particular kind of
expression -- specification, restricted, or initialization as
determined by the check_function. */
bool
gfc_check_constructor (gfc_expr *expr, bool (*check_function) (gfc_expr *))
{
cons_stack *base_save;
bool t;
base_save = base;
base = NULL;
t = check_constructor (expr->value.constructor, check_function);
base = base_save;
return t;
}
/**************** Simplification of array constructors ****************/
iterator_stack *iter_stack;
typedef struct
{
gfc_constructor_base base;
int extract_count, extract_n;
gfc_expr *extracted;
mpz_t *count;
mpz_t *offset;
gfc_component *component;
mpz_t *repeat;
bool (*expand_work_function) (gfc_expr *);
}
expand_info;
static expand_info current_expand;
static bool expand_constructor (gfc_constructor_base);
/* Work function that counts the number of elements present in a
constructor. */
static bool
count_elements (gfc_expr *e)
{
mpz_t result;
if (e->rank == 0)
mpz_add_ui (*current_expand.count, *current_expand.count, 1);
else
{
if (!gfc_array_size (e, &result))
{
gfc_free_expr (e);
return false;
}
mpz_add (*current_expand.count, *current_expand.count, result);
mpz_clear (result);
}
gfc_free_expr (e);
return true;
}
/* Work function that extracts a particular element from an array
constructor, freeing the rest. */
static bool
extract_element (gfc_expr *e)
{
if (e->rank != 0)
{ /* Something unextractable */
gfc_free_expr (e);
return false;
}
if (current_expand.extract_count == current_expand.extract_n)
current_expand.extracted = e;
else
gfc_free_expr (e);
current_expand.extract_count++;
return true;
}
/* Work function that constructs a new constructor out of the old one,
stringing new elements together. */
static bool
expand (gfc_expr *e)
{
gfc_constructor *c = gfc_constructor_append_expr (&current_expand.base,
e, &e->where);
c->n.component = current_expand.component;
return true;
}
/* Given an initialization expression that is a variable reference,
substitute the current value of the iteration variable. */
void
gfc_simplify_iterator_var (gfc_expr *e)
{
iterator_stack *p;
for (p = iter_stack; p; p = p->prev)
if (e->symtree == p->variable)
break;
if (p == NULL)
return; /* Variable not found */
gfc_replace_expr (e, gfc_get_int_expr (gfc_default_integer_kind, NULL, 0));
mpz_set (e->value.integer, p->value);
return;
}
/* Expand an expression with that is inside of a constructor,
recursing into other constructors if present. */
static bool
expand_expr (gfc_expr *e)
{
if (e->expr_type == EXPR_ARRAY)
return expand_constructor (e->value.constructor);
e = gfc_copy_expr (e);
if (!gfc_simplify_expr (e, 1))
{
gfc_free_expr (e);
return false;
}
return current_expand.expand_work_function (e);
}
static bool
expand_iterator (gfc_constructor *c)
{
gfc_expr *start, *end, *step;
iterator_stack frame;
mpz_t trip;
bool t;
end = step = NULL;
t = false;
mpz_init (trip);
mpz_init (frame.value);
frame.prev = NULL;
start = gfc_copy_expr (c->iterator->start);
if (!gfc_simplify_expr (start, 1))
goto cleanup;
if (start->expr_type != EXPR_CONSTANT || start->ts.type != BT_INTEGER)
goto cleanup;
end = gfc_copy_expr (c->iterator->end);
if (!gfc_simplify_expr (end, 1))
goto cleanup;
if (end->expr_type != EXPR_CONSTANT || end->ts.type != BT_INTEGER)
goto cleanup;
step = gfc_copy_expr (c->iterator->step);
if (!gfc_simplify_expr (step, 1))
goto cleanup;
if (step->expr_type != EXPR_CONSTANT || step->ts.type != BT_INTEGER)
goto cleanup;
if (mpz_sgn (step->value.integer) == 0)
{
gfc_error ("Iterator step at %L cannot be zero", &step->where);
goto cleanup;
}
/* Calculate the trip count of the loop. */
mpz_sub (trip, end->value.integer, start->value.integer);
mpz_add (trip, trip, step->value.integer);
mpz_tdiv_q (trip, trip, step->value.integer);
mpz_set (frame.value, start->value.integer);
frame.prev = iter_stack;
frame.variable = c->iterator->var->symtree;
iter_stack = &frame;
while (mpz_sgn (trip) > 0)
{
if (!expand_expr (c->expr))
goto cleanup;
mpz_add (frame.value, frame.value, step->value.integer);
mpz_sub_ui (trip, trip, 1);
}
t = true;
cleanup:
gfc_free_expr (start);
gfc_free_expr (end);
gfc_free_expr (step);
mpz_clear (trip);
mpz_clear (frame.value);
iter_stack = frame.prev;
return t;
}
/* Variables for noticing if all constructors are empty, and
if any of them had a type. */
static bool empty_constructor;
static gfc_typespec empty_ts;
/* Expand a constructor into constant constructors without any
iterators, calling the work function for each of the expanded
expressions. The work function needs to either save or free the
passed expression. */
static bool
expand_constructor (gfc_constructor_base base)
{
gfc_constructor *c;
gfc_expr *e;
for (c = gfc_constructor_first (base); c; c = gfc_constructor_next(c))
{
if (c->iterator != NULL)
{
if (!expand_iterator (c))
return false;
continue;
}
e = c->expr;
if (e == NULL)
return false;
if (empty_constructor)
empty_ts = e->ts;
if (e->expr_type == EXPR_ARRAY)
{
if (!expand_constructor (e->value.constructor))
return false;
continue;
}
empty_constructor = false;
e = gfc_copy_expr (e);
if (!gfc_simplify_expr (e, 1))
{
gfc_free_expr (e);
return false;
}
e->from_constructor = 1;
current_expand.offset = &c->offset;
current_expand.repeat = &c->repeat;
current_expand.component = c->n.component;
if (!current_expand.expand_work_function(e))
return false;
}
return true;
}
/* Given an array expression and an element number (starting at zero),
return a pointer to the array element. NULL is returned if the
size of the array has been exceeded. The expression node returned
remains a part of the array and should not be freed. Access is not
efficient at all, but this is another place where things do not
have to be particularly fast. */
static gfc_expr *
gfc_get_array_element (gfc_expr *array, int element)
{
expand_info expand_save;
gfc_expr *e;
bool rc;
expand_save = current_expand;
current_expand.extract_n = element;
current_expand.expand_work_function = extract_element;
current_expand.extracted = NULL;
current_expand.extract_count = 0;
iter_stack = NULL;
rc = expand_constructor (array->value.constructor);
e = current_expand.extracted;
current_expand = expand_save;
if (!rc)
return NULL;
return e;
}
/* Top level subroutine for expanding constructors. We only expand
constructor if they are small enough. */
bool
gfc_expand_constructor (gfc_expr *e, bool fatal)
{
expand_info expand_save;
gfc_expr *f;
bool rc;
/* If we can successfully get an array element at the max array size then
the array is too big to expand, so we just return. */
f = gfc_get_array_element (e, flag_max_array_constructor);
if (f != NULL)
{
gfc_free_expr (f);
if (fatal)
{
gfc_error ("The number of elements in the array constructor "
"at %L requires an increase of the allowed %d "
"upper limit. See %<-fmax-array-constructor%> "
"option", &e->where, flag_max_array_constructor);
return false;
}
return true;
}
/* We now know the array is not too big so go ahead and try to expand it. */
expand_save = current_expand;
current_expand.base = NULL;
iter_stack = NULL;
empty_constructor = true;
gfc_clear_ts (&empty_ts);
current_expand.expand_work_function = expand;
if (!expand_constructor (e->value.constructor))
{
gfc_constructor_free (current_expand.base);
rc = false;
goto done;
}
/* If we don't have an explicit constructor type, and there
were only empty constructors, then take the type from
them. */
if (constructor_ts.type == BT_UNKNOWN && empty_constructor)
e->ts = empty_ts;
gfc_constructor_free (e->value.constructor);
e->value.constructor = current_expand.base;
rc = true;
done:
current_expand = expand_save;
return rc;
}
/* Work function for checking that an element of a constructor is a
constant, after removal of any iteration variables. We return
false if not so. */
static bool
is_constant_element (gfc_expr *e)
{
int rv;
rv = gfc_is_constant_expr (e);
gfc_free_expr (e);
return rv ? true : false;
}
/* Given an array constructor, determine if the constructor is
constant or not by expanding it and making sure that all elements
are constants. This is a bit of a hack since something like (/ (i,
i=1,100000000) /) will take a while as* opposed to a more clever
function that traverses the expression tree. FIXME. */
int
gfc_constant_ac (gfc_expr *e)
{
expand_info expand_save;
bool rc;
iter_stack = NULL;
expand_save = current_expand;
current_expand.expand_work_function = is_constant_element;
rc = expand_constructor (e->value.constructor);
current_expand = expand_save;
if (!rc)
return 0;
return 1;
}
/* Returns nonzero if an array constructor has been completely
expanded (no iterators) and zero if iterators are present. */
int
gfc_expanded_ac (gfc_expr *e)
{
gfc_constructor *c;
if (e->expr_type == EXPR_ARRAY)
for (c = gfc_constructor_first (e->value.constructor);
c; c = gfc_constructor_next (c))
if (c->iterator != NULL || !gfc_expanded_ac (c->expr))
return 0;
return 1;
}
/*************** Type resolution of array constructors ***************/
/* The symbol expr_is_sought_symbol_ref will try to find. */
static const gfc_symbol *sought_symbol = NULL;
/* Tells whether the expression E is a variable reference to the symbol
in the static variable SOUGHT_SYMBOL, and sets the locus pointer WHERE
accordingly.
To be used with gfc_expr_walker: if a reference is found we don't need
to look further so we return 1 to skip any further walk. */
static int
expr_is_sought_symbol_ref (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
void *where)
{
gfc_expr *expr = *e;
locus *sym_loc = (locus *)where;
if (expr->expr_type == EXPR_VARIABLE
&& expr->symtree->n.sym == sought_symbol)
{
*sym_loc = expr->where;
return 1;
}
return 0;
}
/* Tells whether the expression EXPR contains a reference to the symbol
SYM and in that case sets the position SYM_LOC where the reference is. */
static bool
find_symbol_in_expr (gfc_symbol *sym, gfc_expr *expr, locus *sym_loc)
{
int ret;
sought_symbol = sym;
ret = gfc_expr_walker (&expr, &expr_is_sought_symbol_ref, sym_loc);
sought_symbol = NULL;
return ret;
}
/* Recursive array list resolution function. All of the elements must
be of the same type. */
static bool
resolve_array_list (gfc_constructor_base base)
{
bool t;
gfc_constructor *c;
gfc_iterator *iter;
t = true;
for (c = gfc_constructor_first (base); c; c = gfc_constructor_next (c))
{
iter = c->iterator;
if (iter != NULL)
{
gfc_symbol *iter_var;
locus iter_var_loc;
if (!gfc_resolve_iterator (iter, false, true))
t = false;
/* Check for bounds referencing the iterator variable. */
gcc_assert (iter->var->expr_type == EXPR_VARIABLE);
iter_var = iter->var->symtree->n.sym;
if (find_symbol_in_expr (iter_var, iter->start, &iter_var_loc))
{
if (!gfc_notify_std (GFC_STD_LEGACY, "AC-IMPLIED-DO initial "
"expression references control variable "
"at %L", &iter_var_loc))
t = false;
}
if (find_symbol_in_expr (iter_var, iter->end, &iter_var_loc))
{
if (!gfc_notify_std (GFC_STD_LEGACY, "AC-IMPLIED-DO final "
"expression references control variable "
"at %L", &iter_var_loc))
t = false;
}
if (find_symbol_in_expr (iter_var, iter->step, &iter_var_loc))
{
if (!gfc_notify_std (GFC_STD_LEGACY, "AC-IMPLIED-DO step "
"expression references control variable "
"at %L", &iter_var_loc))
t = false;
}
}
if (!gfc_resolve_expr (c->expr))
t = false;
if (UNLIMITED_POLY (c->expr))
{
gfc_error ("Array constructor value at %L shall not be unlimited "
"polymorphic [F2008: C4106]", &c->expr->where);
t = false;
}
}
return t;
}
/* Resolve character array constructor. If it has a specified constant character
length, pad/truncate the elements here; if the length is not specified and
all elements are of compile-time known length, emit an error as this is
invalid. */
bool
gfc_resolve_character_array_constructor (gfc_expr *expr)
{
gfc_constructor *p;
HOST_WIDE_INT found_length;
gcc_assert (expr->expr_type == EXPR_ARRAY);
gcc_assert (expr->ts.type == BT_CHARACTER);
if (expr->ts.u.cl == NULL)
{
for (p = gfc_constructor_first (expr->value.constructor);
p; p = gfc_constructor_next (p))
if (p->expr->ts.u.cl != NULL)
{
/* Ensure that if there is a char_len around that it is
used; otherwise the middle-end confuses them! */
expr->ts.u.cl = p->expr->ts.u.cl;
goto got_charlen;
}
expr->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL);
}
got_charlen:
/* Early exit for zero size arrays. */
if (expr->shape)
{
mpz_t size;
HOST_WIDE_INT arraysize;
gfc_array_size (expr, &size);
arraysize = mpz_get_ui (size);
mpz_clear (size);
if (arraysize == 0)
return true;
}
found_length = -1;
if (expr->ts.u.cl->length == NULL)
{
/* Check that all constant string elements have the same length until
we reach the end or find a variable-length one. */
for (p = gfc_constructor_first (expr->value.constructor);
p; p = gfc_constructor_next (p))
{
HOST_WIDE_INT current_length = -1;
gfc_ref *ref;
for (ref = p->expr->ref; ref; ref = ref->next)
if (ref->type == REF_SUBSTRING
&& ref->u.ss.start
&& ref->u.ss.start->expr_type == EXPR_CONSTANT
&& ref->u.ss.end
&& ref->u.ss.end->expr_type == EXPR_CONSTANT)
break;
if (p->expr->expr_type == EXPR_CONSTANT)
current_length = p->expr->value.character.length;
else if (ref)
current_length = gfc_mpz_get_hwi (ref->u.ss.end->value.integer)
- gfc_mpz_get_hwi (ref->u.ss.start->value.integer) + 1;
else if (p->expr->ts.u.cl && p->expr->ts.u.cl->length
&& p->expr->ts.u.cl->length->expr_type == EXPR_CONSTANT)
current_length = gfc_mpz_get_hwi (p->expr->ts.u.cl->length->value.integer);
else
return true;
if (current_length < 0)
current_length = 0;
if (found_length == -1)
found_length = current_length;
else if (found_length != current_length)
{
gfc_error ("Different CHARACTER lengths (%ld/%ld) in array"
" constructor at %L", (long) found_length,
(long) current_length, &p->expr->where);
return false;
}
gcc_assert (found_length == current_length);
}
gcc_assert (found_length != -1);
/* Update the character length of the array constructor. */
expr->ts.u.cl->length = gfc_get_int_expr (gfc_charlen_int_kind,
NULL, found_length);
}
else
{
/* We've got a character length specified. It should be an integer,
otherwise an error is signalled elsewhere. */
gcc_assert (expr->ts.u.cl->length);
/* If we've got a constant character length, pad according to this.
gfc_extract_int does check for BT_INTEGER and EXPR_CONSTANT and sets
max_length only if they pass. */
gfc_extract_hwi (expr->ts.u.cl->length, &found_length);
/* Now pad/truncate the elements accordingly to the specified character
length. This is ok inside this conditional, as in the case above
(without typespec) all elements are verified to have the same length
anyway. */
if (found_length != -1)
for (p = gfc_constructor_first (expr->value.constructor);
p; p = gfc_constructor_next (p))
if (p->expr->expr_type == EXPR_CONSTANT)
{
gfc_expr *cl = NULL;
HOST_WIDE_INT current_length = -1;
bool has_ts;
if (p->expr->ts.u.cl && p->expr->ts.u.cl->length)
{
cl = p->expr->ts.u.cl->length;
gfc_extract_hwi (cl, &current_length);
}
/* If gfc_extract_int above set current_length, we implicitly
know the type is BT_INTEGER and it's EXPR_CONSTANT. */
has_ts = expr->ts.u.cl->length_from_typespec;
if (! cl
|| (current_length != -1 && current_length != found_length))
gfc_set_constant_character_len (found_length, p->expr,
has_ts ? -1 : found_length);
}
}
return true;
}
/* Resolve all of the expressions in an array list. */
bool
gfc_resolve_array_constructor (gfc_expr *expr)
{
bool t;
t = resolve_array_list (expr->value.constructor);
if (t)
t = gfc_check_constructor_type (expr);
/* gfc_resolve_character_array_constructor is called in gfc_resolve_expr after
the call to this function, so we don't need to call it here; if it was
called twice, an error message there would be duplicated. */
return t;
}
/* Copy an iterator structure. */
gfc_iterator *
gfc_copy_iterator (gfc_iterator *src)
{
gfc_iterator *dest;
if (src == NULL)
return NULL;
dest = gfc_get_iterator ();
dest->var = gfc_copy_expr (src->var);
dest->start = gfc_copy_expr (src->start);
dest->end = gfc_copy_expr (src->end);
dest->step = gfc_copy_expr (src->step);
dest->unroll = src->unroll;
dest->ivdep = src->ivdep;
dest->vector = src->vector;
dest->novector = src->novector;
return dest;
}
/********* Subroutines for determining the size of an array *********/
/* These are needed just to accommodate RESHAPE(). There are no
diagnostics here, we just return a negative number if something
goes wrong. */
/* Get the size of single dimension of an array specification. The
array is guaranteed to be one dimensional. */
bool
spec_dimen_size (gfc_array_spec *as, int dimen, mpz_t *result)
{
if (as == NULL)
return false;
if (dimen < 0 || dimen > as->rank - 1)
gfc_internal_error ("spec_dimen_size(): Bad dimension");
if (as->type != AS_EXPLICIT
|| !as->lower[dimen]
|| !as->upper[dimen])
return false;
if (as->lower[dimen]->expr_type != EXPR_CONSTANT
|| as->upper[dimen]->expr_type != EXPR_CONSTANT
|| as->lower[dimen]->ts.type != BT_INTEGER
|| as->upper[dimen]->ts.type != BT_INTEGER)
return false;
mpz_init (*result);
mpz_sub (*result, as->upper[dimen]->value.integer,
as->lower[dimen]->value.integer);
mpz_add_ui (*result, *result, 1);
return true;
}
bool
spec_size (gfc_array_spec *as, mpz_t *result)
{
mpz_t size;
int d;
if (!as || as->type == AS_ASSUMED_RANK)
return false;
mpz_init_set_ui (*result, 1);
for (d = 0; d < as->rank; d++)
{
if (!spec_dimen_size (as, d, &size))
{
mpz_clear (*result);
return false;
}
mpz_mul (*result, *result, size);
mpz_clear (size);
}
return true;
}
/* Get the number of elements in an array section. Optionally, also supply
the end value. */
bool
gfc_ref_dimen_size (gfc_array_ref *ar, int dimen, mpz_t *result, mpz_t *end)
{
mpz_t upper, lower, stride;
mpz_t diff;
bool t;
gfc_expr *stride_expr = NULL;
if (dimen < 0 || ar == NULL)
gfc_internal_error ("gfc_ref_dimen_size(): Bad dimension");
if (dimen > ar->dimen - 1)
{
gfc_error ("Bad array dimension at %L", &ar->c_where[dimen]);
return false;
}
switch (ar->dimen_type[dimen])
{
case DIMEN_ELEMENT:
mpz_init (*result);
mpz_set_ui (*result, 1);
t = true;
break;
case DIMEN_VECTOR:
t = gfc_array_size (ar->start[dimen], result); /* Recurse! */
break;
case DIMEN_RANGE:
mpz_init (stride);
if (ar->stride[dimen] == NULL)
mpz_set_ui (stride, 1);
else
{
stride_expr = gfc_copy_expr(ar->stride[dimen]);
if(!gfc_simplify_expr(stride_expr, 1))
gfc_internal_error("Simplification error");
if (stride_expr->expr_type != EXPR_CONSTANT
|| mpz_cmp_ui (stride_expr->value.integer, 0) == 0)
{
mpz_clear (stride);
return false;
}
mpz_set (stride, stride_expr->value.integer);
gfc_free_expr(stride_expr);
}
/* Calculate the number of elements via gfc_dep_differce, but only if
start and end are both supplied in the reference or the array spec.
This is to guard against strange but valid code like
subroutine foo(a,n)
real a(1:n)
n = 3
print *,size(a(n-1:))
where the user changes the value of a variable. If we have to
determine end as well, we cannot do this using gfc_dep_difference.
Fall back to the constants-only code then. */
if (end == NULL)
{
bool use_dep;
use_dep = gfc_dep_difference (ar->end[dimen], ar->start[dimen],
&diff);
if (!use_dep && ar->end[dimen] == NULL && ar->start[dimen] == NULL)
use_dep = gfc_dep_difference (ar->as->upper[dimen],
ar->as->lower[dimen], &diff);
if (use_dep)
{
mpz_init (*result);
mpz_add (*result, diff, stride);
mpz_div (*result, *result, stride);
if (mpz_cmp_ui (*result, 0) < 0)
mpz_set_ui (*result, 0);
mpz_clear (stride);
mpz_clear (diff);
return true;
}
}
/* Constant-only code here, which covers more cases
like a(:4) etc. */
mpz_init (upper);
mpz_init (lower);
t = false;
if (ar->start[dimen] == NULL)
{
if (ar->as->lower[dimen] == NULL
|| ar->as->lower[dimen]->expr_type != EXPR_CONSTANT
|| ar->as->lower[dimen]->ts.type != BT_INTEGER)
goto cleanup;
mpz_set (lower, ar->as->lower[dimen]->value.integer);
}
else
{
if (ar->start[dimen]->expr_type != EXPR_CONSTANT)
goto cleanup;
mpz_set (lower, ar->start[dimen]->value.integer);
}
if (ar->end[dimen] == NULL)
{
if (ar->as->upper[dimen] == NULL
|| ar->as->upper[dimen]->expr_type != EXPR_CONSTANT
|| ar->as->upper[dimen]->ts.type != BT_INTEGER)
goto cleanup;
mpz_set (upper, ar->as->upper[dimen]->value.integer);
}
else
{
if (ar->end[dimen]->expr_type != EXPR_CONSTANT)
goto cleanup;
mpz_set (upper, ar->end[dimen]->value.integer);
}
mpz_init (*result);
mpz_sub (*result, upper, lower);
mpz_add (*result, *result, stride);
mpz_div (*result, *result, stride);
/* Zero stride caught earlier. */
if (mpz_cmp_ui (*result, 0) < 0)
mpz_set_ui (*result, 0);
t = true;
if (end)
{
mpz_init (*end);
mpz_sub_ui (*end, *result, 1UL);
mpz_mul (*end, *end, stride);
mpz_add (*end, *end, lower);
}
cleanup:
mpz_clear (upper);
mpz_clear (lower);
mpz_clear (stride);
return t;
default:
gfc_internal_error ("gfc_ref_dimen_size(): Bad dimen_type");
}
return t;
}
static bool
ref_size (gfc_array_ref *ar, mpz_t *result)
{
mpz_t size;
int d;
mpz_init_set_ui (*result, 1);
for (d = 0; d < ar->dimen; d++)
{
if (!gfc_ref_dimen_size (ar, d, &size, NULL))
{
mpz_clear (*result);
return false;
}
mpz_mul (*result, *result, size);
mpz_clear (size);
}
return true;
}
/* Given an array expression and a dimension, figure out how many
elements it has along that dimension. Returns true if we were
able to return a result in the 'result' variable, false
otherwise. */
bool
gfc_array_dimen_size (gfc_expr *array, int dimen, mpz_t *result)
{
gfc_ref *ref;
int i;
gcc_assert (array != NULL);
if (array->ts.type == BT_CLASS)
return false;
if (array->rank == -1)
return false;
if (dimen < 0 || dimen > array->rank - 1)
gfc_internal_error ("gfc_array_dimen_size(): Bad dimension");
switch (array->expr_type)
{
case EXPR_VARIABLE:
case EXPR_FUNCTION:
for (ref = array->ref; ref; ref = ref->next)
{
if (ref->type != REF_ARRAY)
continue;
if (ref->u.ar.type == AR_FULL)
return spec_dimen_size (ref->u.ar.as, dimen, result);
if (ref->u.ar.type == AR_SECTION)
{
for (i = 0; dimen >= 0; i++)
if (ref->u.ar.dimen_type[i] != DIMEN_ELEMENT)
dimen--;
return gfc_ref_dimen_size (&ref->u.ar, i - 1, result, NULL);
}
}
if (array->shape)
{
mpz_init_set (*result, array->shape[dimen]);
return true;
}
if (array->symtree->n.sym->attr.generic
&& array->value.function.esym != NULL)
{
if (!spec_dimen_size (array->value.function.esym->as, dimen, result))
return false;
}
else if (!spec_dimen_size (array->symtree->n.sym->as, dimen, result))
return false;
break;
case EXPR_ARRAY:
if (array->shape == NULL) {
/* Expressions with rank > 1 should have "shape" properly set */
if ( array->rank != 1 )
gfc_internal_error ("gfc_array_dimen_size(): Bad EXPR_ARRAY expr");
return gfc_array_size(array, result);
}
/* Fall through */
default:
if (array->shape == NULL)
return false;
mpz_init_set (*result, array->shape[dimen]);
break;
}
return true;
}
/* Given an array expression, figure out how many elements are in the
array. Returns true if this is possible, and sets the 'result'
variable. Otherwise returns false. */
bool
gfc_array_size (gfc_expr *array, mpz_t *result)
{
expand_info expand_save;
gfc_ref *ref;
int i;
bool t;
if (array->ts.type == BT_CLASS)
return false;
switch (array->expr_type)
{
case EXPR_ARRAY:
gfc_push_suppress_errors ();
expand_save = current_expand;
current_expand.count = result;
mpz_init_set_ui (*result, 0);
current_expand.expand_work_function = count_elements;
iter_stack = NULL;
t = expand_constructor (array->value.constructor);
gfc_pop_suppress_errors ();
if (!t)
mpz_clear (*result);
current_expand = expand_save;
return t;
case EXPR_VARIABLE:
for (ref = array->ref; ref; ref = ref->next)
{
if (ref->type != REF_ARRAY)
continue;
if (ref->u.ar.type == AR_FULL)
return spec_size (ref->u.ar.as, result);
if (ref->u.ar.type == AR_SECTION)
return ref_size (&ref->u.ar, result);
}
return spec_size (array->symtree->n.sym->as, result);
default:
if (array->rank == 0 || array->shape == NULL)
return false;
mpz_init_set_ui (*result, 1);
for (i = 0; i < array->rank; i++)
mpz_mul (*result, *result, array->shape[i]);
break;
}
return true;
}
/* Given an array reference, return the shape of the reference in an
array of mpz_t integers. */
bool
gfc_array_ref_shape (gfc_array_ref *ar, mpz_t *shape)
{
int d;
int i;
d = 0;
switch (ar->type)
{
case AR_FULL:
for (; d < ar->as->rank; d++)
if (!spec_dimen_size (ar->as, d, &shape[d]))
goto cleanup;
return true;
case AR_SECTION:
for (i = 0; i < ar->dimen; i++)
{
if (ar->dimen_type[i] != DIMEN_ELEMENT)
{
if (!gfc_ref_dimen_size (ar, i, &shape[d], NULL))
goto cleanup;
d++;
}
}
return true;
default:
break;
}
cleanup:
gfc_clear_shape (shape, d);
return false;
}
/* Given an array expression, find the array reference structure that
characterizes the reference. */
gfc_array_ref *
gfc_find_array_ref (gfc_expr *e, bool allow_null)
{
gfc_ref *ref;
for (ref = e->ref; ref; ref = ref->next)
if (ref->type == REF_ARRAY
&& (ref->u.ar.type == AR_FULL || ref->u.ar.type == AR_SECTION))
break;
if (ref == NULL)
{
if (allow_null)
return NULL;
else
gfc_internal_error ("gfc_find_array_ref(): No ref found");
}
return &ref->u.ar;
}
/* Find out if an array shape is known at compile time. */
bool
gfc_is_compile_time_shape (gfc_array_spec *as)
{
if (as->type != AS_EXPLICIT)
return false;
for (int i = 0; i < as->rank; i++)
if (!gfc_is_constant_expr (as->lower[i])
|| !gfc_is_constant_expr (as->upper[i]))
return false;
return true;
}