/* Deal with I/O statements & related stuff.
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation,
- Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software
+ Foundation, Inc.
Contributed by Andy Vaught
This file is part of GCC.
#include "parse.h"
gfc_st_label format_asterisk =
- { -1, ST_LABEL_FORMAT, ST_LABEL_FORMAT, NULL, 0,
- {NULL, NULL}, NULL, NULL};
+ {0, NULL, NULL, -1, ST_LABEL_FORMAT, ST_LABEL_FORMAT, NULL,
+ 0, {NULL, NULL}};
typedef struct
{
tag_unit = {"UNIT", " unit = %e", BT_INTEGER},
tag_advance = {"ADVANCE", " advance = %e", BT_CHARACTER},
tag_rec = {"REC", " rec = %e", BT_INTEGER},
+ tag_spos = {"POSITION", " pos = %e", BT_INTEGER},
tag_format = {"FORMAT", NULL, BT_CHARACTER},
tag_iomsg = {"IOMSG", " iomsg = %e", BT_CHARACTER},
tag_iostat = {"IOSTAT", " iostat = %v", BT_INTEGER},
tag_s_pad = {"PAD", " pad = %v", BT_CHARACTER},
tag_iolength = {"IOLENGTH", " iolength = %v", BT_INTEGER},
tag_convert = {"CONVERT", " convert = %e", BT_CHARACTER},
+ tag_strm_out = {"POS", " pos = %v", BT_INTEGER},
tag_err = {"ERR", " err = %l", BT_UNKNOWN},
tag_end = {"END", " end = %l", BT_UNKNOWN},
tag_eor = {"EOR", " eor = %l", BT_UNKNOWN};
use_last_char = 1;
}
+/* Eat up the spaces and return a character. */
+
+static char
+next_char_not_space(void)
+{
+ char c;
+ do
+ {
+ c = next_char (0);
+ }
+ while (gfc_is_whitespace (c));
+ return c;
+}
+
static int value = 0;
/* Simple lexical analyzer for getting the next token in a FORMAT
return token;
}
- do
- {
- c = next_char (0);
- }
- while (gfc_is_whitespace (c));
-
+ c = next_char_not_space ();
+
negative_flag = 0;
switch (c)
{
case '-':
negative_flag = 1;
case '+':
- c = next_char (0);
+ c = next_char_not_space ();
if (!ISDIGIT (c))
{
token = FMT_UNKNOWN;
do
{
- c = next_char (0);
+ c = next_char_not_space ();
if(ISDIGIT (c))
value = 10 * value + c - '0';
}
do
{
- c = next_char (0);
+ c = next_char_not_space ();
if (c != '0')
zflag = 0;
if (ISDIGIT (c))
break;
case 'T':
- c = next_char (0);
+ c = next_char_not_space ();
if (c != 'L' && c != 'R')
unget_char ();
break;
case 'S':
- c = next_char (0);
+ c = next_char_not_space ();
if (c != 'P' && c != 'S')
unget_char ();
break;
case 'B':
- c = next_char (0);
+ c = next_char_not_space ();
if (c == 'N' || c == 'Z')
token = FMT_BLANK;
else
break;
case 'E':
- c = next_char (0);
+ c = next_char_not_space ();
if (c == 'N' || c == 'S')
token = FMT_EXT;
else
check_format (void)
{
const char *posint_required = _("Positive width required");
- const char *period_required = _("Period required");
const char *nonneg_required = _("Nonnegative width required");
const char *unexpected_element = _("Unexpected element");
const char *unexpected_end = _("Unexpected end of format string");
if (t == FMT_POSINT)
break;
- error = posint_required;
- goto syntax;
+ switch (gfc_notification_std (GFC_STD_GNU))
+ {
+ case WARNING:
+ gfc_warning
+ ("Extension: Missing positive width after L descriptor at %C");
+ saved_token = t;
+ break;
+
+ case ERROR:
+ error = posint_required;
+ goto syntax;
+
+ case SILENT:
+ saved_token = t;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
case FMT_A:
t = format_lex ();
u = format_lex ();
if (u != FMT_PERIOD)
{
- error = period_required;
- goto syntax;
+ /* Warn if -std=legacy, otherwise error. */
+ if (gfc_option.warn_std != 0)
+ gfc_error_now ("Period required in format specifier at %C");
+ else
+ gfc_warning ("Period required in format specifier at %C");
+ saved_token = u;
+ break;
}
u = format_lex ();
t = format_lex ();
if (t != FMT_PERIOD)
{
- error = period_required;
- goto syntax;
+ /* Warn if -std=legacy, otherwise error. */
+ if (gfc_option.warn_std != 0)
+ gfc_error_now ("Period required in format specifier at %C");
+ else
+ gfc_warning ("Period required in format specifier at %C");
+ saved_token = t;
+ break;
}
t = format_lex ();
gfc_expr *e;
locus start;
+ if (gfc_current_ns->proc_name
+ && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+ {
+ gfc_error ("Format statement in module main block at %C.");
+ return MATCH_ERROR;
+ }
+
if (gfc_statement_label == NULL)
{
gfc_error ("Missing format label at %C");
return MATCH_ERROR;
}
+ if (m == MATCH_YES
+ && gfc_reference_st_label (*label, ST_LABEL_TARGET) == FAILURE)
+ return MATCH_ERROR;
+
return m;
}
return FAILURE;
}
}
+ else if (e->ts.type == BT_INTEGER)
+ {
+ gfc_error ("scalar '%s' FORMAT tag at %L is not an ASSIGNED "
+ "variable", gfc_basic_typename (e->ts.type), &e->where);
+ return FAILURE;
+ }
+
return SUCCESS;
}
else
if (tag == &tag_iostat && e->ts.kind != gfc_default_integer_kind)
{
- if (gfc_notify_std (GFC_STD_F2003, "Fortran 2003: Non-default "
- "integer kind in IOSTAT tag at %L",
+ if (gfc_notify_std (GFC_STD_GNU, "Fortran 95 requires default "
+ "INTEGER in IOSTAT tag at %L",
+ &e->where) == FAILURE)
+ return FAILURE;
+ }
+
+ if (tag == &tag_size && e->ts.kind != gfc_default_integer_kind)
+ {
+ if (gfc_notify_std (GFC_STD_GNU, "Fortran 95 requires default "
+ "INTEGER in SIZE tag at %L",
&e->where) == FAILURE)
return FAILURE;
}
}
+
+/* Check if a given value for a SPECIFIER is either in the list of values
+ allowed in F95 or F2003, issuing an error message and returning a zero
+ value if it is not allowed. */
+static int
+compare_to_allowed_values (const char * specifier, const char * allowed[],
+ const char * allowed_f2003[],
+ const char * allowed_gnu[], char * value,
+ const char * statement, bool warn)
+{
+ int i;
+ unsigned int len;
+
+ len = strlen(value);
+ if (len > 0)
+ {
+ for (len--; len > 0; len--)
+ if (value[len] != ' ')
+ break;
+ len++;
+ }
+
+ for (i = 0; allowed[i]; i++)
+ if (len == strlen(allowed[i])
+ && strncasecmp (value, allowed[i], strlen(allowed[i])) == 0)
+ return 1;
+
+ for (i = 0; allowed_f2003 && allowed_f2003[i]; i++)
+ if (len == strlen(allowed_f2003[i])
+ && strncasecmp (value, allowed_f2003[i], strlen(allowed_f2003[i])) == 0)
+ {
+ notification n = gfc_notification_std (GFC_STD_F2003);
+
+ if (n == WARNING || (warn && n == ERROR))
+ {
+ gfc_warning ("Fortran 2003: %s specifier in %s statement at %C "
+ "has value '%s'", specifier, statement,
+ allowed_f2003[i]);
+ return 1;
+ }
+ else
+ if (n == ERROR)
+ {
+ gfc_notify_std (GFC_STD_F2003, "Fortran 2003: %s specifier in "
+ "%s statement at %C has value '%s'", specifier,
+ statement, allowed_f2003[i]);
+ return 0;
+ }
+
+ /* n == SILENT */
+ return 1;
+ }
+
+ for (i = 0; allowed_gnu && allowed_gnu[i]; i++)
+ if (len == strlen(allowed_gnu[i])
+ && strncasecmp (value, allowed_gnu[i], strlen(allowed_gnu[i])) == 0)
+ {
+ notification n = gfc_notification_std (GFC_STD_GNU);
+
+ if (n == WARNING || (warn && n == ERROR))
+ {
+ gfc_warning ("Extension: %s specifier in %s statement at %C "
+ "has value '%s'", specifier, statement,
+ allowed_gnu[i]);
+ return 1;
+ }
+ else
+ if (n == ERROR)
+ {
+ gfc_notify_std (GFC_STD_GNU, "Extension: %s specifier in "
+ "%s statement at %C has value '%s'", specifier,
+ statement, allowed_gnu[i]);
+ return 0;
+ }
+
+ /* n == SILENT */
+ return 1;
+ }
+
+ if (warn)
+ {
+ gfc_warning ("%s specifier in %s statement at %C has invalid value '%s'",
+ specifier, statement, value);
+ return 1;
+ }
+ else
+ {
+ gfc_error ("%s specifier in %s statement at %C has invalid value '%s'",
+ specifier, statement, value);
+ return 0;
+ }
+}
+
/* Match an OPEN statement. */
match
{
gfc_open *open;
match m;
+ bool warn;
m = gfc_match_char ('(');
if (m == MATCH_NO)
goto cleanup;
}
+ warn = (open->err || open->iostat) ? true : false;
+ /* Checks on the ACCESS specifier. */
+ if (open->access && open->access->expr_type == EXPR_CONSTANT)
+ {
+ static const char * access_f95[] = { "SEQUENTIAL", "DIRECT", NULL };
+ static const char * access_f2003[] = { "STREAM", NULL };
+ static const char * access_gnu[] = { "APPEND", NULL };
+
+ if (!compare_to_allowed_values ("ACCESS", access_f95, access_f2003,
+ access_gnu,
+ open->access->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the ACTION specifier. */
+ if (open->action && open->action->expr_type == EXPR_CONSTANT)
+ {
+ static const char * action[] = { "READ", "WRITE", "READWRITE", NULL };
+
+ if (!compare_to_allowed_values ("ACTION", action, NULL, NULL,
+ open->action->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the ASYNCHRONOUS specifier. */
+ /* TODO: code is ready, just needs uncommenting when async I/O support
+ is added ;-)
+ if (open->asynchronous && open->asynchronous->expr_type == EXPR_CONSTANT)
+ {
+ static const char * asynchronous[] = { "YES", "NO", NULL };
+
+ if (!compare_to_allowed_values
+ ("action", asynchronous, NULL, NULL,
+ open->asynchronous->value.character.string, "OPEN", warn))
+ goto cleanup;
+ }*/
+
+ /* Checks on the BLANK specifier. */
+ if (open->blank && open->blank->expr_type == EXPR_CONSTANT)
+ {
+ static const char * blank[] = { "ZERO", "NULL", NULL };
+
+ if (!compare_to_allowed_values ("BLANK", blank, NULL, NULL,
+ open->blank->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the DECIMAL specifier. */
+ /* TODO: uncomment this code when DECIMAL support is added
+ if (open->decimal && open->decimal->expr_type == EXPR_CONSTANT)
+ {
+ static const char * decimal[] = { "COMMA", "POINT", NULL };
+
+ if (!compare_to_allowed_values ("DECIMAL", decimal, NULL, NULL,
+ open->decimal->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ } */
+
+ /* Checks on the DELIM specifier. */
+ if (open->delim && open->delim->expr_type == EXPR_CONSTANT)
+ {
+ static const char * delim[] = { "APOSTROPHE", "QUOTE", "NONE", NULL };
+
+ if (!compare_to_allowed_values ("DELIM", delim, NULL, NULL,
+ open->delim->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the ENCODING specifier. */
+ /* TODO: uncomment this code when ENCODING support is added
+ if (open->encoding && open->encoding->expr_type == EXPR_CONSTANT)
+ {
+ static const char * encoding[] = { "UTF-8", "DEFAULT", NULL };
+
+ if (!compare_to_allowed_values ("ENCODING", encoding, NULL, NULL,
+ open->encoding->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ } */
+
+ /* Checks on the FORM specifier. */
+ if (open->form && open->form->expr_type == EXPR_CONSTANT)
+ {
+ static const char * form[] = { "FORMATTED", "UNFORMATTED", NULL };
+
+ if (!compare_to_allowed_values ("FORM", form, NULL, NULL,
+ open->form->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the PAD specifier. */
+ if (open->pad && open->pad->expr_type == EXPR_CONSTANT)
+ {
+ static const char * pad[] = { "YES", "NO", NULL };
+
+ if (!compare_to_allowed_values ("PAD", pad, NULL, NULL,
+ open->pad->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the POSITION specifier. */
+ if (open->position && open->position->expr_type == EXPR_CONSTANT)
+ {
+ static const char * position[] = { "ASIS", "REWIND", "APPEND", NULL };
+
+ if (!compare_to_allowed_values ("POSITION", position, NULL, NULL,
+ open->position->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ }
+
+ /* Checks on the ROUND specifier. */
+ /* TODO: uncomment this code when ROUND support is added
+ if (open->round && open->round->expr_type == EXPR_CONSTANT)
+ {
+ static const char * round[] = { "UP", "DOWN", "ZERO", "NEAREST",
+ "COMPATIBLE", "PROCESSOR_DEFINED", NULL };
+
+ if (!compare_to_allowed_values ("ROUND", round, NULL, NULL,
+ open->round->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ } */
+
+ /* Checks on the SIGN specifier. */
+ /* TODO: uncomment this code when SIGN support is added
+ if (open->sign && open->sign->expr_type == EXPR_CONSTANT)
+ {
+ static const char * sign[] = { "PLUS", "SUPPRESS", "PROCESSOR_DEFINED",
+ NULL };
+
+ if (!compare_to_allowed_values ("SIGN", sign, NULL, NULL,
+ open->sign->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+ } */
+
+#define warn_or_error(...) \
+{ \
+ if (warn) \
+ gfc_warning (__VA_ARGS__); \
+ else \
+ { \
+ gfc_error (__VA_ARGS__); \
+ goto cleanup; \
+ } \
+}
+
+ /* Checks on the RECL specifier. */
+ if (open->recl && open->recl->expr_type == EXPR_CONSTANT
+ && open->recl->ts.type == BT_INTEGER
+ && mpz_sgn (open->recl->value.integer) != 1)
+ {
+ warn_or_error ("RECL in OPEN statement at %C must be positive");
+ }
+
+ /* Checks on the STATUS specifier. */
+ if (open->status && open->status->expr_type == EXPR_CONSTANT)
+ {
+ static const char * status[] = { "OLD", "NEW", "SCRATCH",
+ "REPLACE", "UNKNOWN", NULL };
+
+ if (!compare_to_allowed_values ("STATUS", status, NULL, NULL,
+ open->status->value.character.string,
+ "OPEN", warn))
+ goto cleanup;
+
+ /* F2003, 9.4.5: If the STATUS= specifier has the value NEW or REPLACE,
+ the FILE= specifier shall appear. */
+ if (open->file == NULL &&
+ (strncasecmp (open->status->value.character.string, "replace", 7) == 0
+ || strncasecmp (open->status->value.character.string, "new", 3) == 0))
+ {
+ warn_or_error ("The STATUS specified in OPEN statement at %C is '%s' "
+ "and no FILE specifier is present",
+ open->status->value.character.string);
+ }
+
+ /* F2003, 9.4.5: If the STATUS= specifier has the value SCRATCH,
+ the FILE= specifier shall not appear. */
+ if (strncasecmp (open->status->value.character.string, "scratch", 7) == 0
+ && open->file)
+ {
+ warn_or_error ("The STATUS specified in OPEN statement at %C cannot "
+ "have the value SCRATCH if a FILE specifier "
+ "is present");
+ }
+ }
+
+ /* Things that are not allowed for unformatted I/O. */
+ if (open->form && open->form->expr_type == EXPR_CONSTANT
+ && (open->delim
+ /* TODO uncomment this code when F2003 support is finished */
+ /* || open->decimal || open->encoding || open->round
+ || open->sign */
+ || open->pad || open->blank)
+ && strncasecmp (open->form->value.character.string,
+ "unformatted", 11) == 0)
+ {
+ const char * spec = (open->delim ? "DELIM " : (open->pad ? "PAD " :
+ open->blank ? "BLANK " : ""));
+
+ warn_or_error ("%sspecifier at %C not allowed in OPEN statement for "
+ "unformatted I/O", spec);
+ }
+
+ if (open->recl && open->access && open->access->expr_type == EXPR_CONSTANT
+ && strncasecmp (open->access->value.character.string, "stream", 6) == 0)
+ {
+ warn_or_error ("RECL specifier not allowed in OPEN statement at %C for "
+ "stream I/O");
+ }
+
+ if (open->position && open->access && open->access->expr_type == EXPR_CONSTANT
+ && !(strncasecmp (open->access->value.character.string,
+ "sequential", 10) == 0
+ || strncasecmp (open->access->value.character.string,
+ "stream", 6) == 0
+ || strncasecmp (open->access->value.character.string,
+ "append", 6) == 0))
+ {
+ warn_or_error ("POSITION specifier in OPEN statement at %C only allowed "
+ "for stream or sequential ACCESS");
+ }
+
+#undef warn_or_error
+
new_st.op = EXEC_OPEN;
new_st.ext.open = open;
return MATCH_YES;
{
gfc_close *close;
match m;
+ bool warn;
m = gfc_match_char ('(');
if (m == MATCH_NO)
goto cleanup;
}
+ warn = (close->iostat || close->err) ? true : false;
+
+ /* Checks on the STATUS specifier. */
+ if (close->status && close->status->expr_type == EXPR_CONSTANT)
+ {
+ static const char * status[] = { "KEEP", "DELETE", NULL };
+
+ if (!compare_to_allowed_values ("STATUS", status, NULL, NULL,
+ close->status->value.character.string,
+ "CLOSE", warn))
+ goto cleanup;
+ }
+
new_st.op = EXEC_CLOSE;
new_st.ext.close = close;
return MATCH_YES;
m = match_etag (&tag_rec, &dt->rec);
if (m != MATCH_NO)
return m;
+ m = match_etag (&tag_spos, &dt->rec);
+ if (m != MATCH_NO)
+ return m;
m = match_out_tag (&tag_iomsg, &dt->iomsg);
if (m != MATCH_NO)
return m;
if (m != MATCH_NO)
return m;
m = match_ltag (&tag_err, &dt->err);
+ if (m == MATCH_YES)
+ dt->err_where = gfc_current_locus;
if (m != MATCH_NO)
return m;
m = match_etag (&tag_advance, &dt->advance);
RESOLVE_TAG (&tag_format, dt->format_expr);
RESOLVE_TAG (&tag_rec, dt->rec);
+ RESOLVE_TAG (&tag_spos, dt->rec);
RESOLVE_TAG (&tag_advance, dt->advance);
RESOLVE_TAG (&tag_iomsg, dt->iomsg);
RESOLVE_TAG (&tag_iostat, dt->iostat);
return FAILURE;
}
- /* Sanity checks on data transfer statements. */
if (e->ts.type == BT_CHARACTER)
{
if (gfc_has_vector_index (e))
&e->where);
return FAILURE;
}
+ }
- if (dt->rec != NULL)
- {
- gfc_error ("REC tag at %L is incompatible with internal file",
- &dt->rec->where);
- return FAILURE;
- }
-
- if (dt->namelist != NULL)
- {
- gfc_error ("Internal file at %L is incompatible with namelist",
- &dt->io_unit->where);
- return FAILURE;
- }
-
- if (dt->advance != NULL)
- {
- gfc_error ("ADVANCE tag at %L is incompatible with internal file",
- &dt->advance->where);
- return FAILURE;
- }
+ if (e->rank && e->ts.type != BT_CHARACTER)
+ {
+ gfc_error ("External IO UNIT cannot be an array at %L", &e->where);
+ return FAILURE;
}
- if (dt->rec != NULL)
+ if (dt->err)
{
- if (dt->end != NULL)
+ if (gfc_reference_st_label (dt->err, ST_LABEL_TARGET) == FAILURE)
+ return FAILURE;
+ if (dt->err->defined == ST_LABEL_UNKNOWN)
{
- gfc_error ("REC tag at %L is incompatible with END tag",
- &dt->rec->where);
+ gfc_error ("ERR tag label %d at %L not defined",
+ dt->err->value, &dt->err_where);
return FAILURE;
}
+ }
- if (dt->format_label == &format_asterisk)
+ if (dt->end)
+ {
+ if (gfc_reference_st_label (dt->end, ST_LABEL_TARGET) == FAILURE)
+ return FAILURE;
+ if (dt->end->defined == ST_LABEL_UNKNOWN)
{
- gfc_error
- ("END tag at %L is incompatible with list directed format (*)",
- &dt->end_where);
+ gfc_error ("END tag label %d at %L not defined",
+ dt->end->value, &dt->end_where);
return FAILURE;
}
+ }
- if (dt->namelist != NULL)
+ if (dt->eor)
+ {
+ if (gfc_reference_st_label (dt->eor, ST_LABEL_TARGET) == FAILURE)
+ return FAILURE;
+ if (dt->eor->defined == ST_LABEL_UNKNOWN)
{
- gfc_error ("REC tag at %L is incompatible with namelist",
- &dt->rec->where);
+ gfc_error ("EOR tag label %d at %L not defined",
+ dt->eor->value, &dt->eor_where);
return FAILURE;
}
}
- if (dt->advance != NULL && dt->format_label == &format_asterisk)
- {
- gfc_error ("ADVANCE tag at %L is incompatible with list directed "
- "format (*)", &dt->advance->where);
- return FAILURE;
- }
-
- if (dt->eor != 0 && dt->advance == NULL)
- {
- gfc_error ("EOR tag at %L requires an ADVANCE tag", &dt->eor_where);
- return FAILURE;
- }
-
- if (dt->size != NULL && dt->advance == NULL)
- {
- gfc_error ("SIZE tag at %L requires an ADVANCE tag", &dt->size->where);
- return FAILURE;
- }
-
- /* TODO: Make sure the ADVANCE tag is 'yes' or 'no' if it is a string
- constant. */
-
- if (gfc_reference_st_label (dt->err, ST_LABEL_TARGET) == FAILURE)
- return FAILURE;
-
- if (gfc_reference_st_label (dt->end, ST_LABEL_TARGET) == FAILURE)
- return FAILURE;
-
- if (gfc_reference_st_label (dt->eor, ST_LABEL_TARGET) == FAILURE)
- return FAILURE;
-
/* Check the format label actually exists. */
if (dt->format_label && dt->format_label != &format_asterisk
&& dt->format_label->defined == ST_LABEL_UNKNOWN)
}
+/* Check the constraints for a data transfer statement. The majority of the
+ constraints appearing in 9.4 of the standard appear here. Some are handled
+ in resolve_tag and others in gfc_resolve_dt. */
+
+static match
+check_io_constraints (io_kind k, gfc_dt *dt, gfc_code * io_code, locus * spec_end)
+{
+#define io_constraint(condition,msg,arg)\
+if (condition) \
+ {\
+ gfc_error(msg,arg);\
+ m = MATCH_ERROR;\
+ }
+
+ match m;
+ gfc_expr * expr;
+ gfc_symbol * sym = NULL;
+
+ m = MATCH_YES;
+
+ expr = dt->io_unit;
+ if (expr && expr->expr_type == EXPR_VARIABLE
+ && expr->ts.type == BT_CHARACTER)
+ {
+ sym = expr->symtree->n.sym;
+
+ io_constraint (k == M_WRITE && sym->attr.intent == INTENT_IN,
+ "Internal file at %L must not be INTENT(IN)",
+ &expr->where);
+
+ io_constraint (gfc_has_vector_index (dt->io_unit),
+ "Internal file incompatible with vector subscript at %L",
+ &expr->where);
+
+ io_constraint (dt->rec != NULL,
+ "REC tag at %L is incompatible with internal file",
+ &dt->rec->where);
+
+ if (dt->namelist != NULL)
+ {
+ if (gfc_notify_std(GFC_STD_F2003,
+ "Internal file at %L is incompatible with namelist",
+ &expr->where) == FAILURE)
+ m = MATCH_ERROR;
+ }
+
+ io_constraint (dt->advance != NULL,
+ "ADVANCE tag at %L is incompatible with internal file",
+ &dt->advance->where);
+ }
+
+ if (expr && expr->ts.type != BT_CHARACTER)
+ {
+
+ io_constraint (gfc_pure (NULL)
+ && (k == M_READ || k == M_WRITE),
+ "IO UNIT in %s statement at %C must be "
+ "an internal file in a PURE procedure",
+ io_kind_name (k));
+ }
+
+
+ if (k != M_READ)
+ {
+ io_constraint (dt->end,
+ "END tag not allowed with output at %L",
+ &dt->end_where);
+
+ io_constraint (dt->eor,
+ "EOR tag not allowed with output at %L",
+ &dt->eor_where);
+
+ io_constraint (k != M_READ && dt->size,
+ "SIZE=specifier not allowed with output at %L",
+ &dt->size->where);
+ }
+ else
+ {
+ io_constraint (dt->size && dt->advance == NULL,
+ "SIZE tag at %L requires an ADVANCE tag",
+ &dt->size->where);
+
+ io_constraint (dt->eor && dt->advance == NULL,
+ "EOR tag at %L requires an ADVANCE tag",
+ &dt->eor_where);
+ }
+
+
+
+ if (dt->namelist)
+ {
+ io_constraint (io_code && dt->namelist,
+ "NAMELIST cannot be followed by IO-list at %L",
+ &io_code->loc);
+
+ io_constraint (dt->format_expr,
+ "IO spec-list cannot contain both NAMELIST group name "
+ "and format specification at %L.",
+ &dt->format_expr->where);
+
+ io_constraint (dt->format_label,
+ "IO spec-list cannot contain both NAMELIST group name "
+ "and format label at %L", spec_end);
+
+ io_constraint (dt->rec,
+ "NAMELIST IO is not allowed with a REC=specifier "
+ "at %L.", &dt->rec->where);
+
+ io_constraint (dt->advance,
+ "NAMELIST IO is not allowed with a ADVANCE=specifier "
+ "at %L.", &dt->advance->where);
+ }
+
+ if (dt->rec)
+ {
+ io_constraint (dt->end,
+ "An END tag is not allowed with a "
+ "REC=specifier at %L.", &dt->end_where);
+
+
+ io_constraint (dt->format_label == &format_asterisk,
+ "FMT=* is not allowed with a REC=specifier "
+ "at %L.", spec_end);
+ }
+
+ if (dt->advance)
+ {
+ int not_yes, not_no;
+ expr = dt->advance;
+
+ io_constraint (dt->format_label == &format_asterisk,
+ "List directed format(*) is not allowed with a "
+ "ADVANCE=specifier at %L.", &expr->where);
+
+ io_constraint (dt->format_expr == NULL
+ && dt->format_label == NULL
+ && dt->namelist == NULL,
+ "the ADVANCE=specifier at %L must appear with an "
+ "explicit format expression", &expr->where);
+
+ if (expr->expr_type == EXPR_CONSTANT && expr->ts.type == BT_CHARACTER)
+ {
+ const char * advance = expr->value.character.string;
+ not_no = strncasecmp (advance, "no", 2) != 0;
+ not_yes = strncasecmp (advance, "yes", 2) != 0;
+ }
+ else
+ {
+ not_no = 0;
+ not_yes = 0;
+ }
+
+ io_constraint (not_no && not_yes,
+ "ADVANCE=specifier at %L must have value = "
+ "YES or NO.", &expr->where);
+
+ io_constraint (dt->size && not_no && k == M_READ,
+ "SIZE tag at %L requires an ADVANCE = 'NO'",
+ &dt->size->where);
+
+ io_constraint (dt->eor && not_no && k == M_READ,
+ "EOR tag at %L requires an ADVANCE = 'NO'",
+ &dt->eor_where);
+ }
+
+ expr = dt->format_expr;
+ if (expr != NULL && expr->expr_type == EXPR_CONSTANT)
+ check_format_string (expr);
+
+ return m;
+}
+#undef io_constraint
+
/* Match a READ, WRITE or PRINT statement. */
static match
char name[GFC_MAX_SYMBOL_LEN + 1];
gfc_code *io_code;
gfc_symbol *sym;
- gfc_expr *expr;
int comma_flag, c;
locus where;
+ locus spec_end;
gfc_dt *dt;
match m;
+ where = gfc_current_locus;
comma_flag = 0;
current_dt = dt = gfc_getmem (sizeof (gfc_dt));
- if (gfc_match_char ('(') == MATCH_NO)
+ m = gfc_match_char ('(');
+ if (m == MATCH_NO)
{
where = gfc_current_locus;
if (k == M_WRITE)
m = MATCH_ERROR;
goto cleanup;
}
- if (gfc_match_eos () == MATCH_NO)
- {
- gfc_error ("Namelist followed by I/O list at %C");
- m = MATCH_ERROR;
- goto cleanup;
- }
dt->io_unit = default_unit (k);
dt->namelist = sym;
dt->io_unit = default_unit (k);
goto get_io_list;
}
+ else
+ {
+ /* Before issuing an error for a malformed 'print (1,*)' type of
+ error, check for a default-char-expr of the form ('(I0)'). */
+
+ if (k == M_PRINT && m == MATCH_YES)
+ {
+ /* Reset current locus to get the initial '(' in an expression. */
+ gfc_current_locus = where;
+ dt->format_expr = NULL;
+ m = match_dt_format (dt);
+
+ if (m == MATCH_ERROR)
+ goto cleanup;
+ if (m == MATCH_NO || dt->format_expr == NULL)
+ goto syntax;
+
+ comma_flag = 1;
+ dt->io_unit = default_unit (k);
+ goto get_io_list;
+ }
+ }
/* Match a control list */
if (match_dt_element (k, dt) == MATCH_YES)
}
get_io_list:
+
+ /* Used in check_io_constraints, where no locus is available. */
+ spec_end = gfc_current_locus;
+
/* Optional leading comma (non-standard). */
if (!comma_flag
&& gfc_match_char (',') == MATCH_YES
goto syntax;
}
- /* A full IO statement has been matched. */
- if (dt->io_unit->expr_type == EXPR_VARIABLE
- && k == M_WRITE
- && dt->io_unit->ts.type == BT_CHARACTER
- && dt->io_unit->symtree->n.sym->attr.intent == INTENT_IN)
- {
- gfc_error ("Internal file '%s' at %L is INTENT(IN)",
- dt->io_unit->symtree->n.sym->name, &dt->io_unit->where);
- m = MATCH_ERROR;
- goto cleanup;
- }
-
- expr = dt->format_expr;
+ /* A full IO statement has been matched. Check the constraints. spec_end is
+ supplied for cases where no locus is supplied. */
+ m = check_io_constraints (k, dt, io_code, &spec_end);
- if (expr != NULL && expr->expr_type == EXPR_CONSTANT)
- check_format_string (expr);
-
- if (gfc_pure (NULL)
- && (k == M_READ || k == M_WRITE)
- && dt->io_unit->ts.type != BT_CHARACTER)
- {
- gfc_error
- ("io-unit in %s statement at %C must be an internal file in a "
- "PURE procedure", io_kind_name (k));
- m = MATCH_ERROR;
- goto cleanup;
- }
+ if (m == MATCH_ERROR)
+ goto cleanup;
new_st.op = (k == M_READ) ? EXEC_READ : EXEC_WRITE;
new_st.ext.dt = dt;
gfc_free_expr (inquire->pad);
gfc_free_expr (inquire->iolength);
gfc_free_expr (inquire->convert);
+ gfc_free_expr (inquire->strm_pos);
gfc_free (inquire);
}
RETM m = match_vtag (&tag_s_pad, &inquire->pad);
RETM m = match_vtag (&tag_iolength, &inquire->iolength);
RETM m = match_vtag (&tag_convert, &inquire->convert);
+ RETM m = match_out_tag (&tag_strm_out, &inquire->strm_pos);
RETM return MATCH_NO;
}
RESOLVE_TAG (&tag_s_pad, inquire->pad);
RESOLVE_TAG (&tag_iolength, inquire->iolength);
RESOLVE_TAG (&tag_convert, inquire->convert);
+ RESOLVE_TAG (&tag_strm_out, inquire->strm_pos);
if (gfc_reference_st_label (inquire->err, ST_LABEL_TARGET) == FAILURE)
return FAILURE;