summaryrefslogtreecommitdiff
path: root/gcc/fortran/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/fortran/expr.c')
-rw-r--r--gcc/fortran/expr.c1954
1 files changed, 1954 insertions, 0 deletions
diff --git a/gcc/fortran/expr.c b/gcc/fortran/expr.c
new file mode 100644
index 00000000000..78a8dc29998
--- /dev/null
+++ b/gcc/fortran/expr.c
@@ -0,0 +1,1954 @@
+/* Routines for manipulation of expression nodes.
+ Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Contributed by Andy Vaught
+
+This file is part of GNU G95.
+
+GNU G95 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 2, or (at your option)
+any later version.
+
+GNU G95 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 GNU G95; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "config.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "gfortran.h"
+#include "arith.h"
+#include "match.h"
+
+/* Get a new expr node. */
+
+gfc_expr *
+gfc_get_expr (void)
+{
+ gfc_expr *e;
+
+ e = gfc_getmem (sizeof (gfc_expr));
+
+ gfc_clear_ts (&e->ts);
+ e->op1 = NULL;
+ e->op2 = NULL;
+ e->shape = NULL;
+ e->ref = NULL;
+ e->symtree = NULL;
+ e->uop = NULL;
+
+ return e;
+}
+
+
+/* Free an argument list and everything below it. */
+
+void
+gfc_free_actual_arglist (gfc_actual_arglist * a1)
+{
+ gfc_actual_arglist *a2;
+
+ while (a1)
+ {
+ a2 = a1->next;
+ gfc_free_expr (a1->expr);
+ gfc_free (a1);
+ a1 = a2;
+ }
+}
+
+
+/* Copy an arglist structure and all of the arguments. */
+
+gfc_actual_arglist *
+gfc_copy_actual_arglist (gfc_actual_arglist * p)
+{
+ gfc_actual_arglist *head, *tail, *new;
+
+ head = tail = NULL;
+
+ for (; p; p = p->next)
+ {
+ new = gfc_get_actual_arglist ();
+ *new = *p;
+
+ new->expr = gfc_copy_expr (p->expr);
+ new->next = NULL;
+
+ if (head == NULL)
+ head = new;
+ else
+ tail->next = new;
+
+ tail = new;
+ }
+
+ return head;
+}
+
+
+/* Free a list of reference structures. */
+
+void
+gfc_free_ref_list (gfc_ref * p)
+{
+ gfc_ref *q;
+ int i;
+
+ for (; p; p = q)
+ {
+ q = p->next;
+
+ switch (p->type)
+ {
+ case REF_ARRAY:
+ for (i = 0; i < GFC_MAX_DIMENSIONS; i++)
+ {
+ gfc_free_expr (p->u.ar.start[i]);
+ gfc_free_expr (p->u.ar.end[i]);
+ gfc_free_expr (p->u.ar.stride[i]);
+ }
+
+ break;
+
+ case REF_SUBSTRING:
+ gfc_free_expr (p->u.ss.start);
+ gfc_free_expr (p->u.ss.end);
+ break;
+
+ case REF_COMPONENT:
+ break;
+ }
+
+ gfc_free (p);
+ }
+}
+
+
+/* Workhorse function for gfc_free_expr() that frees everything
+ beneath an expression node, but not the node itself. This is
+ useful when we want to simplify a node and replace it with
+ something else or the expression node belongs to another structure. */
+
+static void
+free_expr0 (gfc_expr * e)
+{
+ int n;
+
+ switch (e->expr_type)
+ {
+ case EXPR_CONSTANT:
+ switch (e->ts.type)
+ {
+ case BT_INTEGER:
+ mpz_clear (e->value.integer);
+ break;
+
+ case BT_REAL:
+ mpf_clear (e->value.real);
+ break;
+
+ case BT_CHARACTER:
+ gfc_free (e->value.character.string);
+ break;
+
+ case BT_COMPLEX:
+ mpf_clear (e->value.complex.r);
+ mpf_clear (e->value.complex.i);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case EXPR_OP:
+ if (e->op1 != NULL)
+ gfc_free_expr (e->op1);
+ if (e->op2 != NULL)
+ gfc_free_expr (e->op2);
+ break;
+
+ case EXPR_FUNCTION:
+ gfc_free_actual_arglist (e->value.function.actual);
+ break;
+
+ case EXPR_VARIABLE:
+ break;
+
+ case EXPR_ARRAY:
+ case EXPR_STRUCTURE:
+ gfc_free_constructor (e->value.constructor);
+ break;
+
+ case EXPR_SUBSTRING:
+ gfc_free (e->value.character.string);
+ break;
+
+ case EXPR_NULL:
+ break;
+
+ default:
+ gfc_internal_error ("free_expr0(): Bad expr type");
+ }
+
+ /* Free a shape array. */
+ if (e->shape != NULL)
+ {
+ for (n = 0; n < e->rank; n++)
+ mpz_clear (e->shape[n]);
+
+ gfc_free (e->shape);
+ }
+
+ gfc_free_ref_list (e->ref);
+
+ memset (e, '\0', sizeof (gfc_expr));
+}
+
+
+/* Free an expression node and everything beneath it. */
+
+void
+gfc_free_expr (gfc_expr * e)
+{
+
+ if (e == NULL)
+ return;
+
+ free_expr0 (e);
+ gfc_free (e);
+}
+
+
+/* Graft the *src expression onto the *dest subexpression. */
+
+void
+gfc_replace_expr (gfc_expr * dest, gfc_expr * src)
+{
+
+ free_expr0 (dest);
+ *dest = *src;
+
+ gfc_free (src);
+}
+
+
+/* Try to extract an integer constant from the passed expression node.
+ Returns an error message or NULL if the result is set. It is
+ tempting to generate an error and return SUCCESS or FAILURE, but
+ failure is OK for some callers. */
+
+const char *
+gfc_extract_int (gfc_expr * expr, int *result)
+{
+
+ if (expr->expr_type != EXPR_CONSTANT)
+ return "Constant expression required at %C";
+
+ if (expr->ts.type != BT_INTEGER)
+ return "Integer expression required at %C";
+
+ if ((mpz_cmp_si (expr->value.integer, INT_MAX) > 0)
+ || (mpz_cmp_si (expr->value.integer, INT_MIN) < 0))
+ {
+ return "Integer value too large in expression at %C";
+ }
+
+ *result = (int) mpz_get_si (expr->value.integer);
+
+ return NULL;
+}
+
+
+/* Recursively copy a list of reference structures. */
+
+static gfc_ref *
+copy_ref (gfc_ref * src)
+{
+ gfc_array_ref *ar;
+ gfc_ref *dest;
+
+ if (src == NULL)
+ return NULL;
+
+ dest = gfc_get_ref ();
+ dest->type = src->type;
+
+ switch (src->type)
+ {
+ case REF_ARRAY:
+ ar = gfc_copy_array_ref (&src->u.ar);
+ dest->u.ar = *ar;
+ gfc_free (ar);
+ break;
+
+ case REF_COMPONENT:
+ dest->u.c = src->u.c;
+ break;
+
+ case REF_SUBSTRING:
+ dest->u.ss = src->u.ss;
+ dest->u.ss.start = gfc_copy_expr (src->u.ss.start);
+ dest->u.ss.end = gfc_copy_expr (src->u.ss.end);
+ break;
+ }
+
+ dest->next = copy_ref (src->next);
+
+ return dest;
+}
+
+
+/* Copy a shape array. */
+
+mpz_t *
+gfc_copy_shape (mpz_t * shape, int rank)
+{
+ mpz_t *new_shape;
+ int n;
+
+ if (shape == NULL)
+ return NULL;
+
+ new_shape = gfc_get_shape (rank);
+
+ for (n = 0; n < rank; n++)
+ mpz_init_set (new_shape[n], shape[n]);
+
+ return new_shape;
+}
+
+
+/* Given an expression pointer, return a copy of the expression. This
+ subroutine is recursive. */
+
+gfc_expr *
+gfc_copy_expr (gfc_expr * p)
+{
+ gfc_expr *q;
+ char *s;
+
+ if (p == NULL)
+ return NULL;
+
+ q = gfc_get_expr ();
+ *q = *p;
+
+ switch (q->expr_type)
+ {
+ case EXPR_SUBSTRING:
+ s = gfc_getmem (p->value.character.length + 1);
+ q->value.character.string = s;
+
+ memcpy (s, p->value.character.string, p->value.character.length + 1);
+
+ q->op1 = gfc_copy_expr (p->op1);
+ q->op2 = gfc_copy_expr (p->op2);
+ break;
+
+ case EXPR_CONSTANT:
+ switch (q->ts.type)
+ {
+ case BT_INTEGER:
+ mpz_init_set (q->value.integer, p->value.integer);
+ break;
+
+ case BT_REAL:
+ mpf_init_set (q->value.real, p->value.real);
+ break;
+
+ case BT_COMPLEX:
+ mpf_init_set (q->value.complex.r, p->value.complex.r);
+ mpf_init_set (q->value.complex.i, p->value.complex.i);
+ break;
+
+ case BT_CHARACTER:
+ s = gfc_getmem (p->value.character.length + 1);
+ q->value.character.string = s;
+
+ memcpy (s, p->value.character.string,
+ p->value.character.length + 1);
+ break;
+
+ case BT_LOGICAL:
+ case BT_DERIVED:
+ break; /* Already done */
+
+ case BT_PROCEDURE:
+ case BT_UNKNOWN:
+ gfc_internal_error ("gfc_copy_expr(): Bad expr node");
+ /* Not reached */
+ }
+
+ break;
+
+ case EXPR_OP:
+ switch (q->operator)
+ {
+ case INTRINSIC_NOT:
+ case INTRINSIC_UPLUS:
+ case INTRINSIC_UMINUS:
+ q->op1 = gfc_copy_expr (p->op1);
+ break;
+
+ default: /* Binary operators */
+ q->op1 = gfc_copy_expr (p->op1);
+ q->op2 = gfc_copy_expr (p->op2);
+ break;
+ }
+
+ break;
+
+ case EXPR_FUNCTION:
+ q->value.function.actual =
+ gfc_copy_actual_arglist (p->value.function.actual);
+ break;
+
+ case EXPR_STRUCTURE:
+ case EXPR_ARRAY:
+ q->value.constructor = gfc_copy_constructor (p->value.constructor);
+ break;
+
+ case EXPR_VARIABLE:
+ case EXPR_NULL:
+ break;
+ }
+
+ q->shape = gfc_copy_shape (p->shape, p->rank);
+
+ q->ref = copy_ref (p->ref);
+
+ return q;
+}
+
+
+/* Return the maximum kind of two expressions. In general, higher
+ kind numbers mean more precision for numeric types. */
+
+int
+gfc_kind_max (gfc_expr * e1, gfc_expr * e2)
+{
+
+ return (e1->ts.kind > e2->ts.kind) ? e1->ts.kind : e2->ts.kind;
+}
+
+
+/* Returns nonzero if the type is numeric, zero otherwise. */
+
+static int
+numeric_type (bt type)
+{
+
+ return type == BT_COMPLEX || type == BT_REAL || type == BT_INTEGER;
+}
+
+
+/* Returns nonzero if the typespec is a numeric type, zero otherwise. */
+
+int
+gfc_numeric_ts (gfc_typespec * ts)
+{
+
+ return numeric_type (ts->type);
+}
+
+
+/* Returns an expression node that is an integer constant. */
+
+gfc_expr *
+gfc_int_expr (int i)
+{
+ gfc_expr *p;
+
+ p = gfc_get_expr ();
+
+ p->expr_type = EXPR_CONSTANT;
+ p->ts.type = BT_INTEGER;
+ p->ts.kind = gfc_default_integer_kind ();
+
+ p->where = *gfc_current_locus ();
+ mpz_init_set_si (p->value.integer, i);
+
+ return p;
+}
+
+
+/* Returns an expression node that is a logical constant. */
+
+gfc_expr *
+gfc_logical_expr (int i, locus * where)
+{
+ gfc_expr *p;
+
+ p = gfc_get_expr ();
+
+ p->expr_type = EXPR_CONSTANT;
+ p->ts.type = BT_LOGICAL;
+ p->ts.kind = gfc_default_logical_kind ();
+
+ if (where == NULL)
+ where = gfc_current_locus ();
+ p->where = *where;
+ p->value.logical = i;
+
+ return p;
+}
+
+
+/* Return an expression node with an optional argument list attached.
+ A variable number of gfc_expr pointers are strung together in an
+ argument list with a NULL pointer terminating the list. */
+
+gfc_expr *
+gfc_build_conversion (gfc_expr * e)
+{
+ gfc_expr *p;
+
+ p = gfc_get_expr ();
+ p->expr_type = EXPR_FUNCTION;
+ p->symtree = NULL;
+ p->value.function.actual = NULL;
+
+ p->value.function.actual = gfc_get_actual_arglist ();
+ p->value.function.actual->expr = e;
+
+ return p;
+}
+
+
+/* Given an expression node with some sort of numeric binary
+ expression, insert type conversions required to make the operands
+ have the same type.
+
+ The exception is that the operands of an exponential don't have to
+ have the same type. If possible, the base is promoted to the type
+ of the exponent. For example, 1**2.3 becomes 1.0**2.3, but
+ 1.0**2 stays as it is. */
+
+void
+gfc_type_convert_binary (gfc_expr * e)
+{
+ gfc_expr *op1, *op2;
+
+ op1 = e->op1;
+ op2 = e->op2;
+
+ if (op1->ts.type == BT_UNKNOWN || op2->ts.type == BT_UNKNOWN)
+ {
+ gfc_clear_ts (&e->ts);
+ return;
+ }
+
+ /* Kind conversions of same type. */
+ if (op1->ts.type == op2->ts.type)
+ {
+
+ if (op1->ts.kind == op2->ts.kind)
+ {
+ /* No type conversions. */
+ e->ts = op1->ts;
+ goto done;
+ }
+
+ if (op1->ts.kind > op2->ts.kind)
+ gfc_convert_type (op2, &op1->ts, 2);
+ else
+ gfc_convert_type (op1, &op2->ts, 2);
+
+ e->ts = op1->ts;
+ goto done;
+ }
+
+ /* Integer combined with real or complex. */
+ if (op2->ts.type == BT_INTEGER)
+ {
+ e->ts = op1->ts;
+
+ /* Special cose for ** operator. */
+ if (e->operator == INTRINSIC_POWER)
+ goto done;
+
+ gfc_convert_type (e->op2, &e->ts, 2);
+ goto done;
+ }
+
+ if (op1->ts.type == BT_INTEGER)
+ {
+ e->ts = op2->ts;
+ gfc_convert_type (e->op1, &e->ts, 2);
+ goto done;
+ }
+
+ /* Real combined with complex. */
+ e->ts.type = BT_COMPLEX;
+ if (op1->ts.kind > op2->ts.kind)
+ e->ts.kind = op1->ts.kind;
+ else
+ e->ts.kind = op2->ts.kind;
+ if (op1->ts.type != BT_COMPLEX || op1->ts.kind != e->ts.kind)
+ gfc_convert_type (e->op1, &e->ts, 2);
+ if (op2->ts.type != BT_COMPLEX || op2->ts.kind != e->ts.kind)
+ gfc_convert_type (e->op2, &e->ts, 2);
+
+done:
+ return;
+}
+
+
+/* Function to determine if an expression is constant or not. This
+ function expects that the expression has already been simplified. */
+
+int
+gfc_is_constant_expr (gfc_expr * e)
+{
+ gfc_constructor *c;
+ gfc_actual_arglist *arg;
+ int rv;
+
+ if (e == NULL)
+ return 1;
+
+ switch (e->expr_type)
+ {
+ case EXPR_OP:
+ rv = (gfc_is_constant_expr (e->op1)
+ && (e->op2 == NULL
+ || gfc_is_constant_expr (e->op2)));
+
+ break;
+
+ case EXPR_VARIABLE:
+ rv = 0;
+ break;
+
+ case EXPR_FUNCTION:
+ /* Call to intrinsic with at least one argument. */
+ rv = 0;
+ if (e->value.function.isym && e->value.function.actual)
+ {
+ for (arg = e->value.function.actual; arg; arg = arg->next)
+ {
+ if (!gfc_is_constant_expr (arg->expr))
+ break;
+ }
+ if (arg == NULL)
+ rv = 1;
+ }
+ break;
+
+ case EXPR_CONSTANT:
+ case EXPR_NULL:
+ rv = 1;
+ break;
+
+ case EXPR_SUBSTRING:
+ rv = gfc_is_constant_expr (e->op1) && gfc_is_constant_expr (e->op2);
+ break;
+
+ case EXPR_STRUCTURE:
+ rv = 0;
+ for (c = e->value.constructor; c; c = c->next)
+ if (!gfc_is_constant_expr (c->expr))
+ break;
+
+ if (c == NULL)
+ rv = 1;
+ break;
+
+ case EXPR_ARRAY:
+ rv = gfc_constant_ac (e);
+ break;
+
+ default:
+ gfc_internal_error ("gfc_is_constant_expr(): Unknown expression type");
+ }
+
+ return rv;
+}
+
+
+/* Try to collapse intrinsic expressions. */
+
+static try
+simplify_intrinsic_op (gfc_expr * p, int type)
+{
+ gfc_expr *op1, *op2, *result;
+
+ if (p->operator == INTRINSIC_USER)
+ return SUCCESS;
+
+ op1 = p->op1;
+ op2 = p->op2;
+
+ if (gfc_simplify_expr (op1, type) == FAILURE)
+ return FAILURE;
+ if (gfc_simplify_expr (op2, type) == FAILURE)
+ return FAILURE;
+
+ if (!gfc_is_constant_expr (op1)
+ || (op2 != NULL && !gfc_is_constant_expr (op2)))
+ return SUCCESS;
+
+ /* Rip p apart */
+ p->op1 = NULL;
+ p->op2 = NULL;
+
+ switch (p->operator)
+ {
+ case INTRINSIC_UPLUS:
+ result = gfc_uplus (op1);
+ break;
+
+ case INTRINSIC_UMINUS:
+ result = gfc_uminus (op1);
+ break;
+
+ case INTRINSIC_PLUS:
+ result = gfc_add (op1, op2);
+ break;
+
+ case INTRINSIC_MINUS:
+ result = gfc_subtract (op1, op2);
+ break;
+
+ case INTRINSIC_TIMES:
+ result = gfc_multiply (op1, op2);
+ break;
+
+ case INTRINSIC_DIVIDE:
+ result = gfc_divide (op1, op2);
+ break;
+
+ case INTRINSIC_POWER:
+ result = gfc_power (op1, op2);
+ break;
+
+ case INTRINSIC_CONCAT:
+ result = gfc_concat (op1, op2);
+ break;
+
+ case INTRINSIC_EQ:
+ result = gfc_eq (op1, op2);
+ break;
+
+ case INTRINSIC_NE:
+ result = gfc_ne (op1, op2);
+ break;
+
+ case INTRINSIC_GT:
+ result = gfc_gt (op1, op2);
+ break;
+
+ case INTRINSIC_GE:
+ result = gfc_ge (op1, op2);
+ break;
+
+ case INTRINSIC_LT:
+ result = gfc_lt (op1, op2);
+ break;
+
+ case INTRINSIC_LE:
+ result = gfc_le (op1, op2);
+ break;
+
+ case INTRINSIC_NOT:
+ result = gfc_not (op1);
+ break;
+
+ case INTRINSIC_AND:
+ result = gfc_and (op1, op2);
+ break;
+
+ case INTRINSIC_OR:
+ result = gfc_or (op1, op2);
+ break;
+
+ case INTRINSIC_EQV:
+ result = gfc_eqv (op1, op2);
+ break;
+
+ case INTRINSIC_NEQV:
+ result = gfc_neqv (op1, op2);
+ break;
+
+ default:
+ gfc_internal_error ("simplify_intrinsic_op(): Bad operator");
+ }
+
+ if (result == NULL)
+ {
+ gfc_free_expr (op1);
+ gfc_free_expr (op2);
+ return FAILURE;
+ }
+
+ gfc_replace_expr (p, result);
+
+ return SUCCESS;
+}
+
+
+/* Subroutine to simplify constructor expressions. Mutually recursive
+ with gfc_simplify_expr(). */
+
+static try
+simplify_constructor (gfc_constructor * c, int type)
+{
+
+ for (; c; c = c->next)
+ {
+ if (c->iterator
+ && (gfc_simplify_expr (c->iterator->start, type) == FAILURE
+ || gfc_simplify_expr (c->iterator->end, type) == FAILURE
+ || gfc_simplify_expr (c->iterator->step, type) == FAILURE))
+ return FAILURE;
+
+ if (c->expr && gfc_simplify_expr (c->expr, type) == FAILURE)
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+
+/* Pull a single array element out of an array constructor. */
+
+static gfc_constructor *
+find_array_element (gfc_constructor * cons, gfc_array_ref * ar)
+{
+ unsigned long nelemen;
+ int i;
+ mpz_t delta;
+ mpz_t offset;
+
+ mpz_init_set_ui (offset, 0);
+ mpz_init (delta);
+ for (i = 0; i < ar->dimen; i++)
+ {
+ if (ar->start[i]->expr_type != EXPR_CONSTANT)
+ {
+ cons = NULL;
+ break;
+ }
+ mpz_sub (delta, ar->start[i]->value.integer,
+ ar->as->lower[i]->value.integer);
+ mpz_add (offset, offset, delta);
+ }
+
+ if (cons)
+ {
+ if (mpz_fits_ulong_p (offset))
+ {
+ for (nelemen = mpz_get_ui (offset); nelemen > 0; nelemen--)
+ {
+ if (cons->iterator)
+ {
+ cons = NULL;
+ break;
+ }
+ cons = cons->next;
+ }
+ }
+ else
+ cons = NULL;
+ }
+
+ mpz_clear (delta);
+ mpz_clear (offset);
+
+ return cons;
+}
+
+
+/* Find a component of a structure constructor. */
+
+static gfc_constructor *
+find_component_ref (gfc_constructor * cons, gfc_ref * ref)
+{
+ gfc_component *comp;
+ gfc_component *pick;
+
+ comp = ref->u.c.sym->components;
+ pick = ref->u.c.component;
+ while (comp != pick)
+ {
+ comp = comp->next;
+ cons = cons->next;
+ }
+
+ return cons;
+}
+
+
+/* Replace an expression with the contents of a constructor, removing
+ the subobject reference in the process. */
+
+static void
+remove_subobject_ref (gfc_expr * p, gfc_constructor * cons)
+{
+ gfc_expr *e;
+
+ e = cons->expr;
+ cons->expr = NULL;
+ e->ref = p->ref->next;
+ p->ref->next = NULL;
+ gfc_replace_expr (p, e);
+}
+
+
+/* Simplify a subobject reference of a constructor. This occurs when
+ parameter variable values are substituted. */
+
+static try
+simplify_const_ref (gfc_expr * p)
+{
+ gfc_constructor *cons;
+
+ while (p->ref)
+ {
+ switch (p->ref->type)
+ {
+ case REF_ARRAY:
+ switch (p->ref->u.ar.type)
+ {
+ case AR_ELEMENT:
+ cons = find_array_element (p->value.constructor, &p->ref->u.ar);
+ if (!cons)
+ return SUCCESS;
+ remove_subobject_ref (p, cons);
+ break;
+
+ case AR_FULL:
+ if (p->ref->next != NULL)
+ {
+ /* TODO: Simplify array subobject references. */
+ return SUCCESS;
+ }
+ gfc_free_ref_list (p->ref);
+ p->ref = NULL;
+ break;
+
+ default:
+ /* TODO: Simplify array subsections. */
+ return SUCCESS;
+ }
+
+ break;
+
+ case REF_COMPONENT:
+ cons = find_component_ref (p->value.constructor, p->ref);
+ remove_subobject_ref (p, cons);
+ break;
+
+ case REF_SUBSTRING:
+ /* TODO: Constant substrings. */
+ return SUCCESS;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/* Simplify a chain of references. */
+
+static try
+simplify_ref_chain (gfc_ref * ref, int type)
+{
+ int n;
+
+ for (; ref; ref = ref->next)
+ {
+ switch (ref->type)
+ {
+ case REF_ARRAY:
+ for (n = 0; n < ref->u.ar.dimen; n++)
+ {
+ if (gfc_simplify_expr (ref->u.ar.start[n], type)
+ == FAILURE)
+ return FAILURE;
+ if (gfc_simplify_expr (ref->u.ar.end[n], type)
+ == FAILURE)
+ return FAILURE;
+ if (gfc_simplify_expr (ref->u.ar.stride[n], type)
+ == FAILURE)
+ return FAILURE;
+ }
+ break;
+
+ case REF_SUBSTRING:
+ if (gfc_simplify_expr (ref->u.ss.start, type) == FAILURE)
+ return FAILURE;
+ if (gfc_simplify_expr (ref->u.ss.end, type) == FAILURE)
+ return FAILURE;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return SUCCESS;
+}
+
+
+/* Try to substitute the value of a parameter variable. */
+static try
+simplify_parameter_variable (gfc_expr * p, int type)
+{
+ gfc_expr *e;
+ try t;
+
+ e = gfc_copy_expr (p->symtree->n.sym->value);
+ if (p->ref)
+ e->ref = copy_ref (p->ref);
+ t = gfc_simplify_expr (e, type);
+
+ /* Only use the simplification if it eliminated all subobject
+ references. */
+ if (t == SUCCESS && ! e->ref)
+ gfc_replace_expr (p, e);
+ else
+ gfc_free_expr (e);
+
+ return t;
+}
+
+/* Given an expression, simplify it by collapsing constant
+ expressions. Most simplification takes place when the expression
+ tree is being constructed. If an intrinsic function is simplified
+ at some point, we get called again to collapse the result against
+ other constants.
+
+ We work by recursively simplifying expression nodes, simplifying
+ intrinsic functions where possible, which can lead to further
+ constant collapsing. If an operator has constant operand(s), we
+ rip the expression apart, and rebuild it, hoping that it becomes
+ something simpler.
+
+ The expression type is defined for:
+ 0 Basic expression parsing
+ 1 Simplifying array constructors -- will substitute
+ iterator values.
+ Returns FAILURE on error, SUCCESS otherwise.
+ NOTE: Will return SUCCESS even if the expression can not be simplified. */
+
+try
+gfc_simplify_expr (gfc_expr * p, int type)
+{
+ gfc_actual_arglist *ap;
+
+ if (p == NULL)
+ return SUCCESS;
+
+ switch (p->expr_type)
+ {
+ case EXPR_CONSTANT:
+ case EXPR_NULL:
+ break;
+
+ case EXPR_FUNCTION:
+ for (ap = p->value.function.actual; ap; ap = ap->next)
+ if (gfc_simplify_expr (ap->expr, type) == FAILURE)
+ return FAILURE;
+
+ if (p->value.function.isym != NULL
+ && gfc_intrinsic_func_interface (p, 1) == MATCH_ERROR)
+ return FAILURE;
+
+ break;
+
+ case EXPR_SUBSTRING:
+ if (gfc_simplify_expr (p->op1, type) == FAILURE
+ || gfc_simplify_expr (p->op2, type) == FAILURE)
+ return FAILURE;
+
+ /* TODO: evaluate constant substrings. */
+
+ break;
+
+ case EXPR_OP:
+ if (simplify_intrinsic_op (p, type) == FAILURE)
+ return FAILURE;
+ break;
+
+ case EXPR_VARIABLE:
+ /* Only substitute array parameter variables if we are in an
+ initialization expression, or we want a subsection. */
+ if (p->symtree->n.sym->attr.flavor == FL_PARAMETER
+ && (gfc_init_expr || p->ref
+ || p->symtree->n.sym->value->expr_type != EXPR_ARRAY))
+ {
+ if (simplify_parameter_variable (p, type) == FAILURE)
+ return FAILURE;
+ break;
+ }
+
+ if (type == 1)
+ {
+ gfc_simplify_iterator_var (p);
+ }
+
+ /* Simplify subcomponent references. */
+ if (simplify_ref_chain (p->ref, type) == FAILURE)
+ return FAILURE;
+
+ break;
+
+ case EXPR_STRUCTURE:
+ case EXPR_ARRAY:
+ if (simplify_ref_chain (p->ref, type) == FAILURE)
+ return FAILURE;
+
+ if (simplify_constructor (p->value.constructor, type) == FAILURE)
+ return FAILURE;
+
+ if (p->expr_type == EXPR_ARRAY)
+ gfc_expand_constructor (p);
+
+ if (simplify_const_ref (p) == FAILURE)
+ return FAILURE;
+
+ break;
+ }
+
+ return SUCCESS;
+}
+
+
+/* Returns the type of an expression with the exception that iterator
+ variables are automatically integers no matter what else they may
+ be declared as. */
+
+static bt
+et0 (gfc_expr * e)
+{
+
+ if (e->expr_type == EXPR_VARIABLE && gfc_check_iter_variable (e) == SUCCESS)
+ return BT_INTEGER;
+
+ return e->ts.type;
+}
+
+
+/* Check an intrinsic arithmetic operation to see if it is consistent
+ with some type of expression. */
+
+static try check_init_expr (gfc_expr *);
+
+static try
+check_intrinsic_op (gfc_expr * e, try (*check_function) (gfc_expr *))
+{
+
+ if ((*check_function) (e->op1) == FAILURE)
+ return FAILURE;
+
+ switch (e->operator)
+ {
+ case INTRINSIC_UPLUS:
+ case INTRINSIC_UMINUS:
+ if (!numeric_type (et0 (e->op1)))
+ goto not_numeric;
+ break;
+
+ case INTRINSIC_EQ:
+ case INTRINSIC_NE:
+ case INTRINSIC_GT:
+ case INTRINSIC_GE:
+ case INTRINSIC_LT:
+ case INTRINSIC_LE:
+
+ case INTRINSIC_PLUS:
+ case INTRINSIC_MINUS:
+ case INTRINSIC_TIMES:
+ case INTRINSIC_DIVIDE:
+ case INTRINSIC_POWER:
+ if ((*check_function) (e->op2) == FAILURE)
+ return FAILURE;
+
+ if (!numeric_type (et0 (e->op1)) || !numeric_type (et0 (e->op2)))
+ goto not_numeric;
+
+ if (e->operator != INTRINSIC_POWER)
+ break;
+
+ if (check_function == check_init_expr && et0 (e->op2) != BT_INTEGER)
+ {
+ gfc_error ("Exponent at %L must be INTEGER for an initialization "
+ "expression", &e->op2->where);
+ return FAILURE;
+ }
+
+ break;
+
+ case INTRINSIC_CONCAT:
+ if ((*check_function) (e->op2) == FAILURE)
+ return FAILURE;
+
+ if (et0 (e->op1) != BT_CHARACTER || et0 (e->op2) != BT_CHARACTER)
+ {
+ gfc_error ("Concatenation operator in expression at %L "
+ "must have two CHARACTER operands", &e->op1->where);
+ return FAILURE;
+ }
+
+ if (e->op1->ts.kind != e->op2->ts.kind)
+ {
+ gfc_error ("Concat operator at %L must concatenate strings of the "
+ "same kind", &e->where);
+ return FAILURE;
+ }
+
+ break;
+
+ case INTRINSIC_NOT:
+ if (et0 (e->op1) != BT_LOGICAL)
+ {
+ gfc_error (".NOT. operator in expression at %L must have a LOGICAL "
+ "operand", &e->op1->where);
+ return FAILURE;
+ }
+
+ break;
+
+ case INTRINSIC_AND:
+ case INTRINSIC_OR:
+ case INTRINSIC_EQV:
+ case INTRINSIC_NEQV:
+ if ((*check_function) (e->op2) == FAILURE)
+ return FAILURE;
+
+ if (et0 (e->op1) != BT_LOGICAL || et0 (e->op2) != BT_LOGICAL)
+ {
+ gfc_error ("LOGICAL operands are required in expression at %L",
+ &e->where);
+ return FAILURE;
+ }
+
+ break;
+
+ default:
+ gfc_error ("Only intrinsic operators can be used in expression at %L",
+ &e->where);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+
+not_numeric:
+ gfc_error ("Numeric operands are required in expression at %L", &e->where);
+
+ return FAILURE;
+}
+
+
+
+/* Certain inquiry functions are specifically allowed to have variable
+ arguments, which is an exception to the normal requirement that an
+ initialization function have initialization arguments. We head off
+ this problem here. */
+
+static try
+check_inquiry (gfc_expr * e)
+{
+ const char *name;
+
+ /* FIXME: This should be moved into the intrinsic definitions,
+ to eliminate this ugly hack. */
+ static const char * const inquiry_function[] = {
+ "digits", "epsilon", "huge", "kind", "maxexponent", "minexponent",
+ "precision", "radix", "range", "tiny", "bit_size", "size", "shape",
+ "lbound", "ubound", NULL
+ };
+
+ int i;
+
+ /* These functions must have exactly one argument. */
+ if (e->value.function.actual == NULL
+ || e->value.function.actual->next != NULL)
+ return FAILURE;
+
+ if (e->value.function.name != NULL
+ && e->value.function.name[0] != '\0')
+ return FAILURE;
+
+ name = e->symtree->n.sym->name;
+
+ for (i = 0; inquiry_function[i]; i++)
+ if (strcmp (inquiry_function[i], name) == 0)
+ break;
+
+ if (inquiry_function[i] == NULL)
+ return FAILURE;
+
+ e = e->value.function.actual->expr;
+
+ if (e == NULL || e->expr_type != EXPR_VARIABLE)
+ return FAILURE;
+
+ /* At this point we have a numeric inquiry function with a variable
+ argument. The type of the variable might be undefined, but we
+ need it now, because the arguments of these functions are allowed
+ to be undefined. */
+
+ if (e->ts.type == BT_UNKNOWN)
+ {
+ if (e->symtree->n.sym->ts.type == BT_UNKNOWN
+ && gfc_set_default_type (e->symtree->n.sym, 0, gfc_current_ns)
+ == FAILURE)
+ return FAILURE;
+
+ e->ts = e->symtree->n.sym->ts;
+ }
+
+ return SUCCESS;
+}
+
+
+/* Verify that an expression is an initialization expression. A side
+ effect is that the expression tree is reduced to a single constant
+ node if all goes well. This would normally happen when the
+ expression is constructed but function references are assumed to be
+ intrinsics in the context of initialization expressions. If
+ FAILURE is returned an error message has been generated. */
+
+static try
+check_init_expr (gfc_expr * e)
+{
+ gfc_actual_arglist *ap;
+ match m;
+ try t;
+
+ if (e == NULL)
+ return SUCCESS;
+
+ switch (e->expr_type)
+ {
+ case EXPR_OP:
+ t = check_intrinsic_op (e, check_init_expr);
+ if (t == SUCCESS)
+ t = gfc_simplify_expr (e, 0);
+
+ break;
+
+ case EXPR_FUNCTION:
+ t = SUCCESS;
+
+ if (check_inquiry (e) != SUCCESS)
+ {
+ t = SUCCESS;
+ for (ap = e->value.function.actual; ap; ap = ap->next)
+ if (check_init_expr (ap->expr) == FAILURE)
+ {
+ t = FAILURE;
+ break;
+ }
+ }
+
+ if (t == SUCCESS)
+ {
+ m = gfc_intrinsic_func_interface (e, 0);
+
+ if (m == MATCH_NO)
+ gfc_error ("Function '%s' in initialization expression at %L "
+ "must be an intrinsic function",
+ e->symtree->n.sym->name, &e->where);
+
+ if (m != MATCH_YES)
+ t = FAILURE;
+ }
+
+ break;
+
+ case EXPR_VARIABLE:
+ t = SUCCESS;
+
+ if (gfc_check_iter_variable (e) == SUCCESS)
+ break;
+
+ if (e->symtree->n.sym->attr.flavor == FL_PARAMETER)
+ {
+ t = simplify_parameter_variable (e, 0);
+ break;
+ }
+
+ gfc_error ("Variable '%s' at %L cannot appear in an initialization "
+ "expression", e->symtree->n.sym->name, &e->where);
+ t = FAILURE;
+ break;
+
+ case EXPR_CONSTANT:
+ case EXPR_NULL:
+ t = SUCCESS;
+ break;
+
+ case EXPR_SUBSTRING:
+ t = check_init_expr (e->op1);
+ if (t == FAILURE)
+ break;
+
+ t = check_init_expr (e->op2);
+ if (t == SUCCESS)
+ t = gfc_simplify_expr (e, 0);
+
+ break;
+
+ case EXPR_STRUCTURE:
+ t = gfc_check_constructor (e, check_init_expr);
+ break;
+
+ case EXPR_ARRAY:
+ t = gfc_check_constructor (e, check_init_expr);
+ if (t == FAILURE)
+ break;
+
+ t = gfc_expand_constructor (e);
+ if (t == FAILURE)
+ break;
+
+ t = gfc_check_constructor_type (e);
+ break;
+
+ default:
+ gfc_internal_error ("check_init_expr(): Unknown expression type");
+ }
+
+ return t;
+}
+
+
+/* Match an initialization expression. We work by first matching an
+ expression, then reducing it to a constant. */
+
+match
+gfc_match_init_expr (gfc_expr ** result)
+{
+ gfc_expr *expr;
+ match m;
+ try t;
+
+ m = gfc_match_expr (&expr);
+ if (m != MATCH_YES)
+ return m;
+
+ gfc_init_expr = 1;
+ t = gfc_resolve_expr (expr);
+ if (t == SUCCESS)
+ t = check_init_expr (expr);
+ gfc_init_expr = 0;
+
+ if (t == FAILURE)
+ {
+ gfc_free_expr (expr);
+ return MATCH_ERROR;
+ }
+
+ if (expr->expr_type == EXPR_ARRAY
+ && (gfc_check_constructor_type (expr) == FAILURE
+ || gfc_expand_constructor (expr) == FAILURE))
+ {
+ gfc_free_expr (expr);
+ return MATCH_ERROR;
+ }
+
+ if (!gfc_is_constant_expr (expr))
+ gfc_internal_error ("Initialization expression didn't reduce %C");
+
+ *result = expr;
+
+ return MATCH_YES;
+}
+
+
+
+static try check_restricted (gfc_expr *);
+
+/* Given an actual argument list, test to see that each argument is a
+ restricted expression and optionally if the expression type is
+ integer or character. */
+
+static try
+restricted_args (gfc_actual_arglist * a, int check_type)
+{
+ bt type;
+
+ for (; a; a = a->next)
+ {
+ if (check_restricted (a->expr) == FAILURE)
+ return FAILURE;
+
+ if (!check_type)
+ continue;
+
+ type = a->expr->ts.type;
+ if (type != BT_CHARACTER && type != BT_INTEGER)
+ {
+ gfc_error
+ ("Function argument at %L must be of type INTEGER or CHARACTER",
+ &a->expr->where);
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/************* Restricted/specification expressions *************/
+
+
+/* Make sure a non-intrinsic function is a specification function. */
+
+static try
+external_spec_function (gfc_expr * e)
+{
+ gfc_symbol *f;
+
+ f = e->value.function.esym;
+
+ if (f->attr.proc == PROC_ST_FUNCTION)
+ {
+ gfc_error ("Specification function '%s' at %L cannot be a statement "
+ "function", f->name, &e->where);
+ return FAILURE;
+ }
+
+ if (f->attr.proc == PROC_INTERNAL)
+ {
+ gfc_error ("Specification function '%s' at %L cannot be an internal "
+ "function", f->name, &e->where);
+ return FAILURE;
+ }
+
+ if (!f->attr.pure)
+ {
+ gfc_error ("Specification function '%s' at %L must be PURE", f->name,
+ &e->where);
+ return FAILURE;
+ }
+
+ if (f->attr.recursive)
+ {
+ gfc_error ("Specification function '%s' at %L cannot be RECURSIVE",
+ f->name, &e->where);
+ return FAILURE;
+ }
+
+ return restricted_args (e->value.function.actual, 0);
+}
+
+
+/* Check to see that a function reference to an intrinsic is a
+ restricted expression. Some functions required by the standard are
+ omitted because references to them have already been simplified.
+ Strictly speaking, a lot of these checks are redundant with other
+ checks. If a function is indeed a particular intrinsic, then the
+ type of its argument have already been checked and passed. */
+
+static try
+restricted_intrinsic (gfc_expr * e)
+{
+ gfc_intrinsic_sym *sym;
+
+ static struct
+ {
+ const char *name;
+ int case_number;
+ }
+ const *cp, cases[] =
+ {
+ {"repeat", 0},
+ {"reshape", 0},
+ {"selected_int_kind", 0},
+ {"selected_real_kind", 0},
+ {"transfer", 0},
+ {"trim", 0},
+ {"null", 1},
+ {"lbound", 2},
+ {"shape", 2},
+ {"size", 2},
+ {"ubound", 2},
+ /* bit_size() has already been reduced */
+ {"len", 0},
+ /* kind() has already been reduced */
+ /* Numeric inquiry functions have been reduced */
+ { NULL, 0}
+ };
+
+ try t;
+
+ sym = e->value.function.isym;
+ if (!sym)
+ return FAILURE;
+
+ if (sym->elemental)
+ return restricted_args (e->value.function.actual, 1);
+
+ for (cp = cases; cp->name; cp++)
+ if (strcmp (cp->name, sym->name) == 0)
+ break;
+
+ if (cp->name == NULL)
+ {
+ gfc_error ("Intrinsic function '%s' at %L is not a restricted function",
+ sym->name, &e->where);
+ return FAILURE;
+ }
+
+ switch (cp->case_number)
+ {
+ case 0:
+ /* Functions that are restricted if they have character/integer args. */
+ t = restricted_args (e->value.function.actual, 1);
+ break;
+
+ case 1: /* NULL() */
+ t = SUCCESS;
+ break;
+
+ case 2:
+ /* Functions that could be checking the bounds of an assumed-size array. */
+ t = SUCCESS;
+ /* TODO: implement checks from 7.1.6.2 (10) */
+ break;
+
+ default:
+ gfc_internal_error ("restricted_intrinsic(): Bad case");
+ }
+
+ return t;
+}
+
+
+/* Verify that an expression is a restricted expression. Like its
+ cousin check_init_expr(), an error message is generated if we
+ return FAILURE. */
+
+static try
+check_restricted (gfc_expr * e)
+{
+ gfc_symbol *sym;
+ try t;
+
+ if (e == NULL)
+ return SUCCESS;
+
+ switch (e->expr_type)
+ {
+ case EXPR_OP:
+ t = check_intrinsic_op (e, check_restricted);
+ if (t == SUCCESS)
+ t = gfc_simplify_expr (e, 0);
+
+ break;
+
+ case EXPR_FUNCTION:
+ t = e->value.function.esym ?
+ external_spec_function (e) : restricted_intrinsic (e);
+
+ break;
+
+ case EXPR_VARIABLE:
+ sym = e->symtree->n.sym;
+ t = FAILURE;
+
+ if (sym->attr.optional)
+ {
+ gfc_error ("Dummy argument '%s' at %L cannot be OPTIONAL",
+ sym->name, &e->where);
+ break;
+ }
+
+ if (sym->attr.intent == INTENT_OUT)
+ {
+ gfc_error ("Dummy argument '%s' at %L cannot be INTENT(OUT)",
+ sym->name, &e->where);
+ break;
+ }
+
+ if (sym->attr.in_common
+ || sym->attr.use_assoc
+ || sym->attr.dummy
+ || sym->ns != gfc_current_ns
+ || (sym->ns->proc_name != NULL
+ && sym->ns->proc_name->attr.flavor == FL_MODULE))
+ {
+ t = SUCCESS;
+ break;
+ }
+
+ gfc_error ("Variable '%s' cannot appear in the expression at %L",
+ sym->name, &e->where);
+
+ break;
+
+ case EXPR_NULL:
+ case EXPR_CONSTANT:
+ t = SUCCESS;
+ break;
+
+ case EXPR_SUBSTRING:
+ t = gfc_specification_expr (e->op1);
+ if (t == FAILURE)
+ break;
+
+ t = gfc_specification_expr (e->op2);
+ if (t == SUCCESS)
+ t = gfc_simplify_expr (e, 0);
+
+ break;
+
+ case EXPR_STRUCTURE:
+ t = gfc_check_constructor (e, check_restricted);
+ break;
+
+ case EXPR_ARRAY:
+ t = gfc_check_constructor (e, check_restricted);
+ break;
+
+ default:
+ gfc_internal_error ("check_restricted(): Unknown expression type");
+ }
+
+ return t;
+}
+
+
+/* Check to see that an expression is a specification expression. If
+ we return FAILURE, an error has been generated. */
+
+try
+gfc_specification_expr (gfc_expr * e)
+{
+
+ if (e->ts.type != BT_INTEGER)
+ {
+ gfc_error ("Expression at %L must be of INTEGER type", &e->where);
+ return FAILURE;
+ }
+
+ if (e->rank != 0)
+ {
+ gfc_error ("Expression at %L must be scalar", &e->where);
+ return FAILURE;
+ }
+
+ if (gfc_simplify_expr (e, 0) == FAILURE)
+ return FAILURE;
+
+ return check_restricted (e);
+}
+
+
+/************** Expression conformance checks. *************/
+
+/* Given two expressions, make sure that the arrays are conformable. */
+
+try
+gfc_check_conformance (const char *optype, gfc_expr * op1, gfc_expr * op2)
+{
+ int op1_flag, op2_flag, d;
+ mpz_t op1_size, op2_size;
+ try t;
+
+ if (op1->rank == 0 || op2->rank == 0)
+ return SUCCESS;
+
+ if (op1->rank != op2->rank)
+ {
+ gfc_error ("Incompatible ranks in %s at %L", optype, &op1->where);
+ return FAILURE;
+ }
+
+ t = SUCCESS;
+
+ for (d = 0; d < op1->rank; d++)
+ {
+ op1_flag = gfc_array_dimen_size (op1, d, &op1_size) == SUCCESS;
+ op2_flag = gfc_array_dimen_size (op2, d, &op2_size) == SUCCESS;
+
+ if (op1_flag && op2_flag && mpz_cmp (op1_size, op2_size) != 0)
+ {
+ gfc_error ("%s at %L has different shape on dimension %d (%d/%d)",
+ optype, &op1->where, d + 1, (int) mpz_get_si (op1_size),
+ (int) mpz_get_si (op2_size));
+
+ t = FAILURE;
+ }
+
+ if (op1_flag)
+ mpz_clear (op1_size);
+ if (op2_flag)
+ mpz_clear (op2_size);
+
+ if (t == FAILURE)
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+
+/* Given an assignable expression and an arbitrary expression, make
+ sure that the assignment can take place. */
+
+try
+gfc_check_assign (gfc_expr * lvalue, gfc_expr * rvalue, int conform)
+{
+ gfc_symbol *sym;
+
+ sym = lvalue->symtree->n.sym;
+
+ if (sym->attr.intent == INTENT_IN)
+ {
+ gfc_error ("Can't assign to INTENT(IN) variable '%s' at %L",
+ sym->name, &lvalue->where);
+ return FAILURE;
+ }
+
+ if (rvalue->rank != 0 && lvalue->rank != rvalue->rank)
+ {
+ gfc_error ("Incompatible ranks in assignment at %L", &lvalue->where);
+ return FAILURE;
+ }
+
+ if (lvalue->ts.type == BT_UNKNOWN)
+ {
+ gfc_error ("Variable type is UNKNOWN in assignment at %L",
+ &lvalue->where);
+ return FAILURE;
+ }
+
+ /* Check size of array assignments. */
+ if (lvalue->rank != 0 && rvalue->rank != 0
+ && gfc_check_conformance ("Array assignment", lvalue, rvalue) != SUCCESS)
+ return FAILURE;
+
+ if (gfc_compare_types (&lvalue->ts, &rvalue->ts))
+ return SUCCESS;
+
+ if (!conform)
+ {
+ if (gfc_numeric_ts (&lvalue->ts) && gfc_numeric_ts (&rvalue->ts))
+ return SUCCESS;
+
+ gfc_error ("Incompatible types in assignment at %L, %s to %s",
+ &rvalue->where, gfc_typename (&rvalue->ts),
+ gfc_typename (&lvalue->ts));
+
+ return FAILURE;
+ }
+
+ return gfc_convert_type (rvalue, &lvalue->ts, 1);
+}
+
+
+/* Check that a pointer assignment is OK. We first check lvalue, and
+ we only check rvalue if it's not an assignment to NULL() or a
+ NULLIFY statement. */
+
+try
+gfc_check_pointer_assign (gfc_expr * lvalue, gfc_expr * rvalue)
+{
+ symbol_attribute attr;
+ int is_pure;
+
+ if (lvalue->symtree->n.sym->ts.type == BT_UNKNOWN)
+ {
+ gfc_error ("Pointer assignment target is not a POINTER at %L",
+ &lvalue->where);
+ return FAILURE;
+ }
+
+ attr = gfc_variable_attr (lvalue, NULL);
+ if (!attr.pointer)
+ {
+ gfc_error ("Pointer assignment to non-POINTER at %L", &lvalue->where);
+ return FAILURE;
+ }
+
+ is_pure = gfc_pure (NULL);
+
+ if (is_pure && gfc_impure_variable (lvalue->symtree->n.sym))
+ {
+ gfc_error ("Bad pointer object in PURE procedure at %L",
+ &lvalue->where);
+ return FAILURE;
+ }
+
+ /* If rvalue is a NULL() or NULLIFY, we're done. Otherwise the type,
+ kind, etc for lvalue and rvalue must match, and rvalue must be a
+ pure variable if we're in a pure function. */
+ if (rvalue->expr_type != EXPR_NULL)
+ {
+
+ if (!gfc_compare_types (&lvalue->ts, &rvalue->ts))
+ {
+ gfc_error ("Different types in pointer assignment at %L",
+ &lvalue->where);
+ return FAILURE;
+ }
+
+ if (lvalue->ts.kind != rvalue->ts.kind)
+ {
+ gfc_error
+ ("Different kind type parameters in pointer assignment at %L",
+ &lvalue->where);
+ return FAILURE;
+ }
+
+ attr = gfc_expr_attr (rvalue);
+ if (!attr.target && !attr.pointer)
+ {
+ gfc_error
+ ("Pointer assignment target is neither TARGET nor POINTER at "
+ "%L", &rvalue->where);
+ return FAILURE;
+ }
+
+ if (is_pure && gfc_impure_variable (rvalue->symtree->n.sym))
+ {
+ gfc_error
+ ("Bad target in pointer assignment in PURE procedure at %L",
+ &rvalue->where);
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/* Relative of gfc_check_assign() except that the lvalue is a single
+ symbol. */
+
+try
+gfc_check_assign_symbol (gfc_symbol * sym, gfc_expr * rvalue)
+{
+ gfc_expr lvalue;
+ try r;
+
+ memset (&lvalue, '\0', sizeof (gfc_expr));
+
+ lvalue.expr_type = EXPR_VARIABLE;
+ lvalue.ts = sym->ts;
+ if (sym->as)
+ lvalue.rank = sym->as->rank;
+ lvalue.symtree = (gfc_symtree *)gfc_getmem (sizeof (gfc_symtree));
+ lvalue.symtree->n.sym = sym;
+ lvalue.where = sym->declared_at;
+
+ r = gfc_check_assign (&lvalue, rvalue, 1);
+
+ gfc_free (lvalue.symtree);
+
+ return r;
+}