| /* Matching subroutines in all sizes, shapes and colors. |
| Copyright (C) 2000-2013 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 "flags.h" |
| #include "gfortran.h" |
| #include "match.h" |
| #include "parse.h" |
| #include "tree.h" |
| |
| int gfc_matching_ptr_assignment = 0; |
| int gfc_matching_procptr_assignment = 0; |
| bool gfc_matching_prefix = false; |
| |
| /* Stack of SELECT TYPE statements. */ |
| gfc_select_type_stack *select_type_stack = NULL; |
| |
| /* For debugging and diagnostic purposes. Return the textual representation |
| of the intrinsic operator OP. */ |
| const char * |
| gfc_op2string (gfc_intrinsic_op op) |
| { |
| switch (op) |
| { |
| case INTRINSIC_UPLUS: |
| case INTRINSIC_PLUS: |
| return "+"; |
| |
| case INTRINSIC_UMINUS: |
| case INTRINSIC_MINUS: |
| return "-"; |
| |
| case INTRINSIC_POWER: |
| return "**"; |
| case INTRINSIC_CONCAT: |
| return "//"; |
| case INTRINSIC_TIMES: |
| return "*"; |
| case INTRINSIC_DIVIDE: |
| return "/"; |
| |
| case INTRINSIC_AND: |
| return ".and."; |
| case INTRINSIC_OR: |
| return ".or."; |
| case INTRINSIC_EQV: |
| return ".eqv."; |
| case INTRINSIC_NEQV: |
| return ".neqv."; |
| |
| case INTRINSIC_EQ_OS: |
| return ".eq."; |
| case INTRINSIC_EQ: |
| return "=="; |
| case INTRINSIC_NE_OS: |
| return ".ne."; |
| case INTRINSIC_NE: |
| return "/="; |
| case INTRINSIC_GE_OS: |
| return ".ge."; |
| case INTRINSIC_GE: |
| return ">="; |
| case INTRINSIC_LE_OS: |
| return ".le."; |
| case INTRINSIC_LE: |
| return "<="; |
| case INTRINSIC_LT_OS: |
| return ".lt."; |
| case INTRINSIC_LT: |
| return "<"; |
| case INTRINSIC_GT_OS: |
| return ".gt."; |
| case INTRINSIC_GT: |
| return ">"; |
| case INTRINSIC_NOT: |
| return ".not."; |
| |
| case INTRINSIC_ASSIGN: |
| return "="; |
| |
| case INTRINSIC_PARENTHESES: |
| return "parens"; |
| |
| default: |
| break; |
| } |
| |
| gfc_internal_error ("gfc_op2string(): Bad code"); |
| /* Not reached. */ |
| } |
| |
| |
| /******************** Generic matching subroutines ************************/ |
| |
| /* This function scans the current statement counting the opened and closed |
| parenthesis to make sure they are balanced. */ |
| |
| match |
| gfc_match_parens (void) |
| { |
| locus old_loc, where; |
| int count; |
| gfc_instring instring; |
| gfc_char_t c, quote; |
| |
| old_loc = gfc_current_locus; |
| count = 0; |
| instring = NONSTRING; |
| quote = ' '; |
| |
| for (;;) |
| { |
| c = gfc_next_char_literal (instring); |
| if (c == '\n') |
| break; |
| if (quote == ' ' && ((c == '\'') || (c == '"'))) |
| { |
| quote = c; |
| instring = INSTRING_WARN; |
| continue; |
| } |
| if (quote != ' ' && c == quote) |
| { |
| quote = ' '; |
| instring = NONSTRING; |
| continue; |
| } |
| |
| if (c == '(' && quote == ' ') |
| { |
| count++; |
| where = gfc_current_locus; |
| } |
| if (c == ')' && quote == ' ') |
| { |
| count--; |
| where = gfc_current_locus; |
| } |
| } |
| |
| gfc_current_locus = old_loc; |
| |
| if (count > 0) |
| { |
| gfc_error ("Missing ')' in statement at or before %L", &where); |
| return MATCH_ERROR; |
| } |
| if (count < 0) |
| { |
| gfc_error ("Missing '(' in statement at or before %L", &where); |
| return MATCH_ERROR; |
| } |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* See if the next character is a special character that has |
| escaped by a \ via the -fbackslash option. */ |
| |
| match |
| gfc_match_special_char (gfc_char_t *res) |
| { |
| int len, i; |
| gfc_char_t c, n; |
| match m; |
| |
| m = MATCH_YES; |
| |
| switch ((c = gfc_next_char_literal (INSTRING_WARN))) |
| { |
| case 'a': |
| *res = '\a'; |
| break; |
| case 'b': |
| *res = '\b'; |
| break; |
| case 't': |
| *res = '\t'; |
| break; |
| case 'f': |
| *res = '\f'; |
| break; |
| case 'n': |
| *res = '\n'; |
| break; |
| case 'r': |
| *res = '\r'; |
| break; |
| case 'v': |
| *res = '\v'; |
| break; |
| case '\\': |
| *res = '\\'; |
| break; |
| case '0': |
| *res = '\0'; |
| break; |
| |
| case 'x': |
| case 'u': |
| case 'U': |
| /* Hexadecimal form of wide characters. */ |
| len = (c == 'x' ? 2 : (c == 'u' ? 4 : 8)); |
| n = 0; |
| for (i = 0; i < len; i++) |
| { |
| char buf[2] = { '\0', '\0' }; |
| |
| c = gfc_next_char_literal (INSTRING_WARN); |
| if (!gfc_wide_fits_in_byte (c) |
| || !gfc_check_digit ((unsigned char) c, 16)) |
| return MATCH_NO; |
| |
| buf[0] = (unsigned char) c; |
| n = n << 4; |
| n += strtol (buf, NULL, 16); |
| } |
| *res = n; |
| break; |
| |
| default: |
| /* Unknown backslash codes are simply not expanded. */ |
| m = MATCH_NO; |
| break; |
| } |
| |
| return m; |
| } |
| |
| |
| /* In free form, match at least one space. Always matches in fixed |
| form. */ |
| |
| match |
| gfc_match_space (void) |
| { |
| locus old_loc; |
| char c; |
| |
| if (gfc_current_form == FORM_FIXED) |
| return MATCH_YES; |
| |
| old_loc = gfc_current_locus; |
| |
| c = gfc_next_ascii_char (); |
| if (!gfc_is_whitespace (c)) |
| { |
| gfc_current_locus = old_loc; |
| return MATCH_NO; |
| } |
| |
| gfc_gobble_whitespace (); |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match an end of statement. End of statement is optional |
| whitespace, followed by a ';' or '\n' or comment '!'. If a |
| semicolon is found, we continue to eat whitespace and semicolons. */ |
| |
| match |
| gfc_match_eos (void) |
| { |
| locus old_loc; |
| int flag; |
| char c; |
| |
| flag = 0; |
| |
| for (;;) |
| { |
| old_loc = gfc_current_locus; |
| gfc_gobble_whitespace (); |
| |
| c = gfc_next_ascii_char (); |
| switch (c) |
| { |
| case '!': |
| do |
| { |
| c = gfc_next_ascii_char (); |
| } |
| while (c != '\n'); |
| |
| /* Fall through. */ |
| |
| case '\n': |
| return MATCH_YES; |
| |
| case ';': |
| flag = 1; |
| continue; |
| } |
| |
| break; |
| } |
| |
| gfc_current_locus = old_loc; |
| return (flag) ? MATCH_YES : MATCH_NO; |
| } |
| |
| |
| /* Match a literal integer on the input, setting the value on |
| MATCH_YES. Literal ints occur in kind-parameters as well as |
| old-style character length specifications. If cnt is non-NULL it |
| will be set to the number of digits. */ |
| |
| match |
| gfc_match_small_literal_int (int *value, int *cnt) |
| { |
| locus old_loc; |
| char c; |
| int i, j; |
| |
| old_loc = gfc_current_locus; |
| |
| *value = -1; |
| gfc_gobble_whitespace (); |
| c = gfc_next_ascii_char (); |
| if (cnt) |
| *cnt = 0; |
| |
| if (!ISDIGIT (c)) |
| { |
| gfc_current_locus = old_loc; |
| return MATCH_NO; |
| } |
| |
| i = c - '0'; |
| j = 1; |
| |
| for (;;) |
| { |
| old_loc = gfc_current_locus; |
| c = gfc_next_ascii_char (); |
| |
| if (!ISDIGIT (c)) |
| break; |
| |
| i = 10 * i + c - '0'; |
| j++; |
| |
| if (i > 99999999) |
| { |
| gfc_error ("Integer too large at %C"); |
| return MATCH_ERROR; |
| } |
| } |
| |
| gfc_current_locus = old_loc; |
| |
| *value = i; |
| if (cnt) |
| *cnt = j; |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a small, constant integer expression, like in a kind |
| statement. On MATCH_YES, 'value' is set. */ |
| |
| match |
| gfc_match_small_int (int *value) |
| { |
| gfc_expr *expr; |
| const char *p; |
| match m; |
| int i; |
| |
| m = gfc_match_expr (&expr); |
| if (m != MATCH_YES) |
| return m; |
| |
| p = gfc_extract_int (expr, &i); |
| gfc_free_expr (expr); |
| |
| if (p != NULL) |
| { |
| gfc_error (p); |
| m = MATCH_ERROR; |
| } |
| |
| *value = i; |
| return m; |
| } |
| |
| |
| /* This function is the same as the gfc_match_small_int, except that |
| we're keeping the pointer to the expr. This function could just be |
| removed and the previously mentioned one modified, though all calls |
| to it would have to be modified then (and there were a number of |
| them). Return MATCH_ERROR if fail to extract the int; otherwise, |
| return the result of gfc_match_expr(). The expr (if any) that was |
| matched is returned in the parameter expr. */ |
| |
| match |
| gfc_match_small_int_expr (int *value, gfc_expr **expr) |
| { |
| const char *p; |
| match m; |
| int i; |
| |
| m = gfc_match_expr (expr); |
| if (m != MATCH_YES) |
| return m; |
| |
| p = gfc_extract_int (*expr, &i); |
| |
| if (p != NULL) |
| { |
| gfc_error (p); |
| m = MATCH_ERROR; |
| } |
| |
| *value = i; |
| return m; |
| } |
| |
| |
| /* Matches a statement label. Uses gfc_match_small_literal_int() to |
| do most of the work. */ |
| |
| match |
| gfc_match_st_label (gfc_st_label **label) |
| { |
| locus old_loc; |
| match m; |
| int i, cnt; |
| |
| old_loc = gfc_current_locus; |
| |
| m = gfc_match_small_literal_int (&i, &cnt); |
| if (m != MATCH_YES) |
| return m; |
| |
| if (cnt > 5) |
| { |
| gfc_error ("Too many digits in statement label at %C"); |
| goto cleanup; |
| } |
| |
| if (i == 0) |
| { |
| gfc_error ("Statement label at %C is zero"); |
| goto cleanup; |
| } |
| |
| *label = gfc_get_st_label (i); |
| return MATCH_YES; |
| |
| cleanup: |
| |
| gfc_current_locus = old_loc; |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match and validate a label associated with a named IF, DO or SELECT |
| statement. If the symbol does not have the label attribute, we add |
| it. We also make sure the symbol does not refer to another |
| (active) block. A matched label is pointed to by gfc_new_block. */ |
| |
| match |
| gfc_match_label (void) |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| match m; |
| |
| gfc_new_block = NULL; |
| |
| m = gfc_match (" %n :", name); |
| if (m != MATCH_YES) |
| return m; |
| |
| if (gfc_get_symbol (name, NULL, &gfc_new_block)) |
| { |
| gfc_error ("Label name '%s' at %C is ambiguous", name); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_new_block->attr.flavor == FL_LABEL) |
| { |
| gfc_error ("Duplicate construct label '%s' at %C", name); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_add_flavor (&gfc_new_block->attr, FL_LABEL, |
| gfc_new_block->name, NULL) == FAILURE) |
| return MATCH_ERROR; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* See if the current input looks like a name of some sort. Modifies |
| the passed buffer which must be GFC_MAX_SYMBOL_LEN+1 bytes long. |
| Note that options.c restricts max_identifier_length to not more |
| than GFC_MAX_SYMBOL_LEN. */ |
| |
| match |
| gfc_match_name (char *buffer) |
| { |
| locus old_loc; |
| int i; |
| char c; |
| |
| old_loc = gfc_current_locus; |
| gfc_gobble_whitespace (); |
| |
| c = gfc_next_ascii_char (); |
| if (!(ISALPHA (c) || (c == '_' && gfc_option.flag_allow_leading_underscore))) |
| { |
| if (gfc_error_flag_test() == 0 && c != '(') |
| gfc_error ("Invalid character in name at %C"); |
| gfc_current_locus = old_loc; |
| return MATCH_NO; |
| } |
| |
| i = 0; |
| |
| do |
| { |
| buffer[i++] = c; |
| |
| if (i > gfc_option.max_identifier_length) |
| { |
| gfc_error ("Name at %C is too long"); |
| return MATCH_ERROR; |
| } |
| |
| old_loc = gfc_current_locus; |
| c = gfc_next_ascii_char (); |
| } |
| while (ISALNUM (c) || c == '_' || (gfc_option.flag_dollar_ok && c == '$')); |
| |
| if (c == '$' && !gfc_option.flag_dollar_ok) |
| { |
| gfc_error ("Invalid character '$' at %C. Use -fdollar-ok to allow it " |
| "as an extension"); |
| return MATCH_ERROR; |
| } |
| |
| buffer[i] = '\0'; |
| gfc_current_locus = old_loc; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a valid name for C, which is almost the same as for Fortran, |
| except that you can start with an underscore, etc.. It could have |
| been done by modifying the gfc_match_name, but this way other |
| things C allows can be done, such as no limits on the length. |
| Also, by rewriting it, we use the gfc_next_char_C() to prevent the |
| input characters from being automatically lower cased, since C is |
| case sensitive. The parameter, buffer, is used to return the name |
| that is matched. Return MATCH_ERROR if the name is not a valid C |
| name, MATCH_NO if what we're seeing isn't a name, and MATCH_YES if |
| we successfully match a C name. */ |
| |
| match |
| gfc_match_name_C (const char **buffer) |
| { |
| locus old_loc; |
| size_t i = 0; |
| gfc_char_t c; |
| char* buf; |
| size_t cursz = 16; |
| |
| old_loc = gfc_current_locus; |
| gfc_gobble_whitespace (); |
| |
| /* Get the next char (first possible char of name) and see if |
| it's valid for C (either a letter or an underscore). */ |
| c = gfc_next_char_literal (INSTRING_WARN); |
| |
| /* If the user put nothing expect spaces between the quotes, it is valid |
| and simply means there is no name= specifier and the name is the Fortran |
| symbol name, all lowercase. */ |
| if (c == '"' || c == '\'') |
| { |
| gfc_current_locus = old_loc; |
| return MATCH_YES; |
| } |
| |
| if (!ISALPHA (c) && c != '_') |
| { |
| gfc_error ("Invalid C name in NAME= specifier at %C"); |
| return MATCH_ERROR; |
| } |
| |
| buf = XNEWVEC (char, cursz); |
| /* Continue to read valid variable name characters. */ |
| do |
| { |
| gcc_assert (gfc_wide_fits_in_byte (c)); |
| |
| buf[i++] = (unsigned char) c; |
| |
| if (i >= cursz) |
| { |
| cursz *= 2; |
| buf = XRESIZEVEC (char, buf, cursz); |
| } |
| |
| old_loc = gfc_current_locus; |
| |
| /* Get next char; param means we're in a string. */ |
| c = gfc_next_char_literal (INSTRING_WARN); |
| } while (ISALNUM (c) || c == '_'); |
| |
| /* The binding label will be needed later anyway, so just insert it |
| into the symbol table. */ |
| buf[i] = '\0'; |
| *buffer = IDENTIFIER_POINTER (get_identifier (buf)); |
| XDELETEVEC (buf); |
| gfc_current_locus = old_loc; |
| |
| /* See if we stopped because of whitespace. */ |
| if (c == ' ') |
| { |
| gfc_gobble_whitespace (); |
| c = gfc_peek_ascii_char (); |
| if (c != '"' && c != '\'') |
| { |
| gfc_error ("Embedded space in NAME= specifier at %C"); |
| return MATCH_ERROR; |
| } |
| } |
| |
| /* If we stopped because we had an invalid character for a C name, report |
| that to the user by returning MATCH_NO. */ |
| if (c != '"' && c != '\'') |
| { |
| gfc_error ("Invalid C name in NAME= specifier at %C"); |
| return MATCH_ERROR; |
| } |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a symbol on the input. Modifies the pointer to the symbol |
| pointer if successful. */ |
| |
| match |
| gfc_match_sym_tree (gfc_symtree **matched_symbol, int host_assoc) |
| { |
| char buffer[GFC_MAX_SYMBOL_LEN + 1]; |
| match m; |
| |
| m = gfc_match_name (buffer); |
| if (m != MATCH_YES) |
| return m; |
| |
| if (host_assoc) |
| return (gfc_get_ha_sym_tree (buffer, matched_symbol)) |
| ? MATCH_ERROR : MATCH_YES; |
| |
| if (gfc_get_sym_tree (buffer, NULL, matched_symbol, false)) |
| return MATCH_ERROR; |
| |
| return MATCH_YES; |
| } |
| |
| |
| match |
| gfc_match_symbol (gfc_symbol **matched_symbol, int host_assoc) |
| { |
| gfc_symtree *st; |
| match m; |
| |
| m = gfc_match_sym_tree (&st, host_assoc); |
| |
| if (m == MATCH_YES) |
| { |
| if (st) |
| *matched_symbol = st->n.sym; |
| else |
| *matched_symbol = NULL; |
| } |
| else |
| *matched_symbol = NULL; |
| return m; |
| } |
| |
| |
| /* Match an intrinsic operator. Returns an INTRINSIC enum. While matching, |
| we always find INTRINSIC_PLUS before INTRINSIC_UPLUS. We work around this |
| in matchexp.c. */ |
| |
| match |
| gfc_match_intrinsic_op (gfc_intrinsic_op *result) |
| { |
| locus orig_loc = gfc_current_locus; |
| char ch; |
| |
| gfc_gobble_whitespace (); |
| ch = gfc_next_ascii_char (); |
| switch (ch) |
| { |
| case '+': |
| /* Matched "+". */ |
| *result = INTRINSIC_PLUS; |
| return MATCH_YES; |
| |
| case '-': |
| /* Matched "-". */ |
| *result = INTRINSIC_MINUS; |
| return MATCH_YES; |
| |
| case '=': |
| if (gfc_next_ascii_char () == '=') |
| { |
| /* Matched "==". */ |
| *result = INTRINSIC_EQ; |
| return MATCH_YES; |
| } |
| break; |
| |
| case '<': |
| if (gfc_peek_ascii_char () == '=') |
| { |
| /* Matched "<=". */ |
| gfc_next_ascii_char (); |
| *result = INTRINSIC_LE; |
| return MATCH_YES; |
| } |
| /* Matched "<". */ |
| *result = INTRINSIC_LT; |
| return MATCH_YES; |
| |
| case '>': |
| if (gfc_peek_ascii_char () == '=') |
| { |
| /* Matched ">=". */ |
| gfc_next_ascii_char (); |
| *result = INTRINSIC_GE; |
| return MATCH_YES; |
| } |
| /* Matched ">". */ |
| *result = INTRINSIC_GT; |
| return MATCH_YES; |
| |
| case '*': |
| if (gfc_peek_ascii_char () == '*') |
| { |
| /* Matched "**". */ |
| gfc_next_ascii_char (); |
| *result = INTRINSIC_POWER; |
| return MATCH_YES; |
| } |
| /* Matched "*". */ |
| *result = INTRINSIC_TIMES; |
| return MATCH_YES; |
| |
| case '/': |
| ch = gfc_peek_ascii_char (); |
| if (ch == '=') |
| { |
| /* Matched "/=". */ |
| gfc_next_ascii_char (); |
| *result = INTRINSIC_NE; |
| return MATCH_YES; |
| } |
| else if (ch == '/') |
| { |
| /* Matched "//". */ |
| gfc_next_ascii_char (); |
| *result = INTRINSIC_CONCAT; |
| return MATCH_YES; |
| } |
| /* Matched "/". */ |
| *result = INTRINSIC_DIVIDE; |
| return MATCH_YES; |
| |
| case '.': |
| ch = gfc_next_ascii_char (); |
| switch (ch) |
| { |
| case 'a': |
| if (gfc_next_ascii_char () == 'n' |
| && gfc_next_ascii_char () == 'd' |
| && gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".and.". */ |
| *result = INTRINSIC_AND; |
| return MATCH_YES; |
| } |
| break; |
| |
| case 'e': |
| if (gfc_next_ascii_char () == 'q') |
| { |
| ch = gfc_next_ascii_char (); |
| if (ch == '.') |
| { |
| /* Matched ".eq.". */ |
| *result = INTRINSIC_EQ_OS; |
| return MATCH_YES; |
| } |
| else if (ch == 'v') |
| { |
| if (gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".eqv.". */ |
| *result = INTRINSIC_EQV; |
| return MATCH_YES; |
| } |
| } |
| } |
| break; |
| |
| case 'g': |
| ch = gfc_next_ascii_char (); |
| if (ch == 'e') |
| { |
| if (gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".ge.". */ |
| *result = INTRINSIC_GE_OS; |
| return MATCH_YES; |
| } |
| } |
| else if (ch == 't') |
| { |
| if (gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".gt.". */ |
| *result = INTRINSIC_GT_OS; |
| return MATCH_YES; |
| } |
| } |
| break; |
| |
| case 'l': |
| ch = gfc_next_ascii_char (); |
| if (ch == 'e') |
| { |
| if (gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".le.". */ |
| *result = INTRINSIC_LE_OS; |
| return MATCH_YES; |
| } |
| } |
| else if (ch == 't') |
| { |
| if (gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".lt.". */ |
| *result = INTRINSIC_LT_OS; |
| return MATCH_YES; |
| } |
| } |
| break; |
| |
| case 'n': |
| ch = gfc_next_ascii_char (); |
| if (ch == 'e') |
| { |
| ch = gfc_next_ascii_char (); |
| if (ch == '.') |
| { |
| /* Matched ".ne.". */ |
| *result = INTRINSIC_NE_OS; |
| return MATCH_YES; |
| } |
| else if (ch == 'q') |
| { |
| if (gfc_next_ascii_char () == 'v' |
| && gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".neqv.". */ |
| *result = INTRINSIC_NEQV; |
| return MATCH_YES; |
| } |
| } |
| } |
| else if (ch == 'o') |
| { |
| if (gfc_next_ascii_char () == 't' |
| && gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".not.". */ |
| *result = INTRINSIC_NOT; |
| return MATCH_YES; |
| } |
| } |
| break; |
| |
| case 'o': |
| if (gfc_next_ascii_char () == 'r' |
| && gfc_next_ascii_char () == '.') |
| { |
| /* Matched ".or.". */ |
| *result = INTRINSIC_OR; |
| return MATCH_YES; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| gfc_current_locus = orig_loc; |
| return MATCH_NO; |
| } |
| |
| |
| /* Match a loop control phrase: |
| |
| <LVALUE> = <EXPR>, <EXPR> [, <EXPR> ] |
| |
| If the final integer expression is not present, a constant unity |
| expression is returned. We don't return MATCH_ERROR until after |
| the equals sign is seen. */ |
| |
| match |
| gfc_match_iterator (gfc_iterator *iter, int init_flag) |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| gfc_expr *var, *e1, *e2, *e3; |
| locus start; |
| match m; |
| |
| e1 = e2 = e3 = NULL; |
| |
| /* Match the start of an iterator without affecting the symbol table. */ |
| |
| start = gfc_current_locus; |
| m = gfc_match (" %n =", name); |
| gfc_current_locus = start; |
| |
| if (m != MATCH_YES) |
| return MATCH_NO; |
| |
| m = gfc_match_variable (&var, 0); |
| if (m != MATCH_YES) |
| return MATCH_NO; |
| |
| /* F2008, C617 & C565. */ |
| if (var->symtree->n.sym->attr.codimension) |
| { |
| gfc_error ("Loop variable at %C cannot be a coarray"); |
| goto cleanup; |
| } |
| |
| if (var->ref != NULL) |
| { |
| gfc_error ("Loop variable at %C cannot be a sub-component"); |
| goto cleanup; |
| } |
| |
| gfc_match_char ('='); |
| |
| var->symtree->n.sym->attr.implied_index = 1; |
| |
| m = init_flag ? gfc_match_init_expr (&e1) : gfc_match_expr (&e1); |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| if (gfc_match_char (',') != MATCH_YES) |
| goto syntax; |
| |
| m = init_flag ? gfc_match_init_expr (&e2) : gfc_match_expr (&e2); |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| if (gfc_match_char (',') != MATCH_YES) |
| { |
| e3 = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1); |
| goto done; |
| } |
| |
| m = init_flag ? gfc_match_init_expr (&e3) : gfc_match_expr (&e3); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| { |
| gfc_error ("Expected a step value in iterator at %C"); |
| goto cleanup; |
| } |
| |
| done: |
| iter->var = var; |
| iter->start = e1; |
| iter->end = e2; |
| iter->step = e3; |
| return MATCH_YES; |
| |
| syntax: |
| gfc_error ("Syntax error in iterator at %C"); |
| |
| cleanup: |
| gfc_free_expr (e1); |
| gfc_free_expr (e2); |
| gfc_free_expr (e3); |
| |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Tries to match the next non-whitespace character on the input. |
| This subroutine does not return MATCH_ERROR. */ |
| |
| match |
| gfc_match_char (char c) |
| { |
| locus where; |
| |
| where = gfc_current_locus; |
| gfc_gobble_whitespace (); |
| |
| if (gfc_next_ascii_char () == c) |
| return MATCH_YES; |
| |
| gfc_current_locus = where; |
| return MATCH_NO; |
| } |
| |
| |
| /* General purpose matching subroutine. The target string is a |
| scanf-like format string in which spaces correspond to arbitrary |
| whitespace (including no whitespace), characters correspond to |
| themselves. The %-codes are: |
| |
| %% Literal percent sign |
| %e Expression, pointer to a pointer is set |
| %s Symbol, pointer to the symbol is set |
| %n Name, character buffer is set to name |
| %t Matches end of statement. |
| %o Matches an intrinsic operator, returned as an INTRINSIC enum. |
| %l Matches a statement label |
| %v Matches a variable expression (an lvalue) |
| % Matches a required space (in free form) and optional spaces. */ |
| |
| match |
| gfc_match (const char *target, ...) |
| { |
| gfc_st_label **label; |
| int matches, *ip; |
| locus old_loc; |
| va_list argp; |
| char c, *np; |
| match m, n; |
| void **vp; |
| const char *p; |
| |
| old_loc = gfc_current_locus; |
| va_start (argp, target); |
| m = MATCH_NO; |
| matches = 0; |
| p = target; |
| |
| loop: |
| c = *p++; |
| switch (c) |
| { |
| case ' ': |
| gfc_gobble_whitespace (); |
| goto loop; |
| case '\0': |
| m = MATCH_YES; |
| break; |
| |
| case '%': |
| c = *p++; |
| switch (c) |
| { |
| case 'e': |
| vp = va_arg (argp, void **); |
| n = gfc_match_expr ((gfc_expr **) vp); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 'v': |
| vp = va_arg (argp, void **); |
| n = gfc_match_variable ((gfc_expr **) vp, 0); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 's': |
| vp = va_arg (argp, void **); |
| n = gfc_match_symbol ((gfc_symbol **) vp, 0); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 'n': |
| np = va_arg (argp, char *); |
| n = gfc_match_name (np); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 'l': |
| label = va_arg (argp, gfc_st_label **); |
| n = gfc_match_st_label (label); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 'o': |
| ip = va_arg (argp, int *); |
| n = gfc_match_intrinsic_op ((gfc_intrinsic_op *) ip); |
| if (n != MATCH_YES) |
| { |
| m = n; |
| goto not_yes; |
| } |
| |
| matches++; |
| goto loop; |
| |
| case 't': |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| m = MATCH_NO; |
| goto not_yes; |
| } |
| goto loop; |
| |
| case ' ': |
| if (gfc_match_space () == MATCH_YES) |
| goto loop; |
| m = MATCH_NO; |
| goto not_yes; |
| |
| case '%': |
| break; /* Fall through to character matcher. */ |
| |
| default: |
| gfc_internal_error ("gfc_match(): Bad match code %c", c); |
| } |
| |
| default: |
| |
| /* gfc_next_ascii_char converts characters to lower-case, so we shouldn't |
| expect an upper case character here! */ |
| gcc_assert (TOLOWER (c) == c); |
| |
| if (c == gfc_next_ascii_char ()) |
| goto loop; |
| break; |
| } |
| |
| not_yes: |
| va_end (argp); |
| |
| if (m != MATCH_YES) |
| { |
| /* Clean up after a failed match. */ |
| gfc_current_locus = old_loc; |
| va_start (argp, target); |
| |
| p = target; |
| for (; matches > 0; matches--) |
| { |
| while (*p++ != '%'); |
| |
| switch (*p++) |
| { |
| case '%': |
| matches++; |
| break; /* Skip. */ |
| |
| /* Matches that don't have to be undone */ |
| case 'o': |
| case 'l': |
| case 'n': |
| case 's': |
| (void) va_arg (argp, void **); |
| break; |
| |
| case 'e': |
| case 'v': |
| vp = va_arg (argp, void **); |
| gfc_free_expr ((struct gfc_expr *)*vp); |
| *vp = NULL; |
| break; |
| } |
| } |
| |
| va_end (argp); |
| } |
| |
| return m; |
| } |
| |
| |
| /*********************** Statement level matching **********************/ |
| |
| /* Matches the start of a program unit, which is the program keyword |
| followed by an obligatory symbol. */ |
| |
| match |
| gfc_match_program (void) |
| { |
| gfc_symbol *sym; |
| match m; |
| |
| m = gfc_match ("% %s%t", &sym); |
| |
| if (m == MATCH_NO) |
| { |
| gfc_error ("Invalid form of PROGRAM statement at %C"); |
| m = MATCH_ERROR; |
| } |
| |
| if (m == MATCH_ERROR) |
| return m; |
| |
| if (gfc_add_flavor (&sym->attr, FL_PROGRAM, sym->name, NULL) == FAILURE) |
| return MATCH_ERROR; |
| |
| gfc_new_block = sym; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a simple assignment statement. */ |
| |
| match |
| gfc_match_assignment (void) |
| { |
| gfc_expr *lvalue, *rvalue; |
| locus old_loc; |
| match m; |
| |
| old_loc = gfc_current_locus; |
| |
| lvalue = NULL; |
| m = gfc_match (" %v =", &lvalue); |
| if (m != MATCH_YES) |
| { |
| gfc_current_locus = old_loc; |
| gfc_free_expr (lvalue); |
| return MATCH_NO; |
| } |
| |
| rvalue = NULL; |
| m = gfc_match (" %e%t", &rvalue); |
| if (m != MATCH_YES) |
| { |
| gfc_current_locus = old_loc; |
| gfc_free_expr (lvalue); |
| gfc_free_expr (rvalue); |
| return m; |
| } |
| |
| gfc_set_sym_referenced (lvalue->symtree->n.sym); |
| |
| new_st.op = EXEC_ASSIGN; |
| new_st.expr1 = lvalue; |
| new_st.expr2 = rvalue; |
| |
| gfc_check_do_variable (lvalue->symtree); |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a pointer assignment statement. */ |
| |
| match |
| gfc_match_pointer_assignment (void) |
| { |
| gfc_expr *lvalue, *rvalue; |
| locus old_loc; |
| match m; |
| |
| old_loc = gfc_current_locus; |
| |
| lvalue = rvalue = NULL; |
| gfc_matching_ptr_assignment = 0; |
| gfc_matching_procptr_assignment = 0; |
| |
| m = gfc_match (" %v =>", &lvalue); |
| if (m != MATCH_YES) |
| { |
| m = MATCH_NO; |
| goto cleanup; |
| } |
| |
| if (lvalue->symtree->n.sym->attr.proc_pointer |
| || gfc_is_proc_ptr_comp (lvalue)) |
| gfc_matching_procptr_assignment = 1; |
| else |
| gfc_matching_ptr_assignment = 1; |
| |
| m = gfc_match (" %e%t", &rvalue); |
| gfc_matching_ptr_assignment = 0; |
| gfc_matching_procptr_assignment = 0; |
| if (m != MATCH_YES) |
| goto cleanup; |
| |
| new_st.op = EXEC_POINTER_ASSIGN; |
| new_st.expr1 = lvalue; |
| new_st.expr2 = rvalue; |
| |
| return MATCH_YES; |
| |
| cleanup: |
| gfc_current_locus = old_loc; |
| gfc_free_expr (lvalue); |
| gfc_free_expr (rvalue); |
| return m; |
| } |
| |
| |
| /* We try to match an easy arithmetic IF statement. This only happens |
| when just after having encountered a simple IF statement. This code |
| is really duplicate with parts of the gfc_match_if code, but this is |
| *much* easier. */ |
| |
| static match |
| match_arithmetic_if (void) |
| { |
| gfc_st_label *l1, *l2, *l3; |
| gfc_expr *expr; |
| match m; |
| |
| m = gfc_match (" ( %e ) %l , %l , %l%t", &expr, &l1, &l2, &l3); |
| if (m != MATCH_YES) |
| return m; |
| |
| if (gfc_reference_st_label (l1, ST_LABEL_TARGET) == FAILURE |
| || gfc_reference_st_label (l2, ST_LABEL_TARGET) == FAILURE |
| || gfc_reference_st_label (l3, ST_LABEL_TARGET) == FAILURE) |
| { |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_notify_std (GFC_STD_F95_OBS, "Arithmetic IF " |
| "statement at %C") == FAILURE) |
| return MATCH_ERROR; |
| |
| new_st.op = EXEC_ARITHMETIC_IF; |
| new_st.expr1 = expr; |
| new_st.label1 = l1; |
| new_st.label2 = l2; |
| new_st.label3 = l3; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* The IF statement is a bit of a pain. First of all, there are three |
| forms of it, the simple IF, the IF that starts a block and the |
| arithmetic IF. |
| |
| There is a problem with the simple IF and that is the fact that we |
| only have a single level of undo information on symbols. What this |
| means is for a simple IF, we must re-match the whole IF statement |
| multiple times in order to guarantee that the symbol table ends up |
| in the proper state. */ |
| |
| static match match_simple_forall (void); |
| static match match_simple_where (void); |
| |
| match |
| gfc_match_if (gfc_statement *if_type) |
| { |
| gfc_expr *expr; |
| gfc_st_label *l1, *l2, *l3; |
| locus old_loc, old_loc2; |
| gfc_code *p; |
| match m, n; |
| |
| n = gfc_match_label (); |
| if (n == MATCH_ERROR) |
| return n; |
| |
| old_loc = gfc_current_locus; |
| |
| m = gfc_match (" if ( %e", &expr); |
| if (m != MATCH_YES) |
| return m; |
| |
| old_loc2 = gfc_current_locus; |
| gfc_current_locus = old_loc; |
| |
| if (gfc_match_parens () == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| gfc_current_locus = old_loc2; |
| |
| if (gfc_match_char (')') != MATCH_YES) |
| { |
| gfc_error ("Syntax error in IF-expression at %C"); |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| m = gfc_match (" %l , %l , %l%t", &l1, &l2, &l3); |
| |
| if (m == MATCH_YES) |
| { |
| if (n == MATCH_YES) |
| { |
| gfc_error ("Block label not appropriate for arithmetic IF " |
| "statement at %C"); |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_reference_st_label (l1, ST_LABEL_TARGET) == FAILURE |
| || gfc_reference_st_label (l2, ST_LABEL_TARGET) == FAILURE |
| || gfc_reference_st_label (l3, ST_LABEL_TARGET) == FAILURE) |
| { |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_notify_std (GFC_STD_F95_OBS, "Arithmetic IF " |
| "statement at %C") == FAILURE) |
| return MATCH_ERROR; |
| |
| new_st.op = EXEC_ARITHMETIC_IF; |
| new_st.expr1 = expr; |
| new_st.label1 = l1; |
| new_st.label2 = l2; |
| new_st.label3 = l3; |
| |
| *if_type = ST_ARITHMETIC_IF; |
| return MATCH_YES; |
| } |
| |
| if (gfc_match (" then%t") == MATCH_YES) |
| { |
| new_st.op = EXEC_IF; |
| new_st.expr1 = expr; |
| *if_type = ST_IF_BLOCK; |
| return MATCH_YES; |
| } |
| |
| if (n == MATCH_YES) |
| { |
| gfc_error ("Block label is not appropriate for IF statement at %C"); |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| /* At this point the only thing left is a simple IF statement. At |
| this point, n has to be MATCH_NO, so we don't have to worry about |
| re-matching a block label. From what we've got so far, try |
| matching an assignment. */ |
| |
| *if_type = ST_SIMPLE_IF; |
| |
| m = gfc_match_assignment (); |
| if (m == MATCH_YES) |
| goto got_match; |
| |
| gfc_free_expr (expr); |
| gfc_undo_symbols (); |
| gfc_current_locus = old_loc; |
| |
| /* m can be MATCH_NO or MATCH_ERROR, here. For MATCH_ERROR, a mangled |
| assignment was found. For MATCH_NO, continue to call the various |
| matchers. */ |
| if (m == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| gfc_match (" if ( %e ) ", &expr); /* Guaranteed to match. */ |
| |
| m = gfc_match_pointer_assignment (); |
| if (m == MATCH_YES) |
| goto got_match; |
| |
| gfc_free_expr (expr); |
| gfc_undo_symbols (); |
| gfc_current_locus = old_loc; |
| |
| gfc_match (" if ( %e ) ", &expr); /* Guaranteed to match. */ |
| |
| /* Look at the next keyword to see which matcher to call. Matching |
| the keyword doesn't affect the symbol table, so we don't have to |
| restore between tries. */ |
| |
| #define match(string, subr, statement) \ |
| if (gfc_match(string) == MATCH_YES) { m = subr(); goto got_match; } |
| |
| gfc_clear_error (); |
| |
| match ("allocate", gfc_match_allocate, ST_ALLOCATE) |
| match ("assign", gfc_match_assign, ST_LABEL_ASSIGNMENT) |
| match ("backspace", gfc_match_backspace, ST_BACKSPACE) |
| match ("call", gfc_match_call, ST_CALL) |
| match ("close", gfc_match_close, ST_CLOSE) |
| match ("continue", gfc_match_continue, ST_CONTINUE) |
| match ("cycle", gfc_match_cycle, ST_CYCLE) |
| match ("deallocate", gfc_match_deallocate, ST_DEALLOCATE) |
| match ("end file", gfc_match_endfile, ST_END_FILE) |
| match ("error stop", gfc_match_error_stop, ST_ERROR_STOP) |
| match ("exit", gfc_match_exit, ST_EXIT) |
| match ("flush", gfc_match_flush, ST_FLUSH) |
| match ("forall", match_simple_forall, ST_FORALL) |
| match ("go to", gfc_match_goto, ST_GOTO) |
| match ("if", match_arithmetic_if, ST_ARITHMETIC_IF) |
| match ("inquire", gfc_match_inquire, ST_INQUIRE) |
| match ("lock", gfc_match_lock, ST_LOCK) |
| match ("nullify", gfc_match_nullify, ST_NULLIFY) |
| match ("open", gfc_match_open, ST_OPEN) |
| match ("pause", gfc_match_pause, ST_NONE) |
| match ("print", gfc_match_print, ST_WRITE) |
| match ("read", gfc_match_read, ST_READ) |
| match ("return", gfc_match_return, ST_RETURN) |
| match ("rewind", gfc_match_rewind, ST_REWIND) |
| match ("stop", gfc_match_stop, ST_STOP) |
| match ("wait", gfc_match_wait, ST_WAIT) |
| match ("sync all", gfc_match_sync_all, ST_SYNC_CALL); |
| match ("sync images", gfc_match_sync_images, ST_SYNC_IMAGES); |
| match ("sync memory", gfc_match_sync_memory, ST_SYNC_MEMORY); |
| match ("unlock", gfc_match_unlock, ST_UNLOCK) |
| match ("where", match_simple_where, ST_WHERE) |
| match ("write", gfc_match_write, ST_WRITE) |
| |
| /* The gfc_match_assignment() above may have returned a MATCH_NO |
| where the assignment was to a named constant. Check that |
| special case here. */ |
| m = gfc_match_assignment (); |
| if (m == MATCH_NO) |
| { |
| gfc_error ("Cannot assign to a named constant at %C"); |
| gfc_free_expr (expr); |
| gfc_undo_symbols (); |
| gfc_current_locus = old_loc; |
| return MATCH_ERROR; |
| } |
| |
| /* All else has failed, so give up. See if any of the matchers has |
| stored an error message of some sort. */ |
| if (gfc_error_check () == 0) |
| gfc_error ("Unclassifiable statement in IF-clause at %C"); |
| |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| |
| got_match: |
| if (m == MATCH_NO) |
| gfc_error ("Syntax error in IF-clause at %C"); |
| if (m != MATCH_YES) |
| { |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| /* At this point, we've matched the single IF and the action clause |
| is in new_st. Rearrange things so that the IF statement appears |
| in new_st. */ |
| |
| p = gfc_get_code (); |
| p->next = gfc_get_code (); |
| *p->next = new_st; |
| p->next->loc = gfc_current_locus; |
| |
| p->expr1 = expr; |
| p->op = EXEC_IF; |
| |
| gfc_clear_new_st (); |
| |
| new_st.op = EXEC_IF; |
| new_st.block = p; |
| |
| return MATCH_YES; |
| } |
| |
| #undef match |
| |
| |
| /* Match an ELSE statement. */ |
| |
| match |
| gfc_match_else (void) |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| |
| if (gfc_match_eos () == MATCH_YES) |
| return MATCH_YES; |
| |
| if (gfc_match_name (name) != MATCH_YES |
| || gfc_current_block () == NULL |
| || gfc_match_eos () != MATCH_YES) |
| { |
| gfc_error ("Unexpected junk after ELSE statement at %C"); |
| return MATCH_ERROR; |
| } |
| |
| if (strcmp (name, gfc_current_block ()->name) != 0) |
| { |
| gfc_error ("Label '%s' at %C doesn't match IF label '%s'", |
| name, gfc_current_block ()->name); |
| return MATCH_ERROR; |
| } |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match an ELSE IF statement. */ |
| |
| match |
| gfc_match_elseif (void) |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| gfc_expr *expr; |
| match m; |
| |
| m = gfc_match (" ( %e ) then", &expr); |
| if (m != MATCH_YES) |
| return m; |
| |
| if (gfc_match_eos () == MATCH_YES) |
| goto done; |
| |
| if (gfc_match_name (name) != MATCH_YES |
| || gfc_current_block () == NULL |
| || gfc_match_eos () != MATCH_YES) |
| { |
| gfc_error ("Unexpected junk after ELSE IF statement at %C"); |
| goto cleanup; |
| } |
| |
| if (strcmp (name, gfc_current_block ()->name) != 0) |
| { |
| gfc_error ("Label '%s' at %C doesn't match IF label '%s'", |
| name, gfc_current_block ()->name); |
| goto cleanup; |
| } |
| |
| done: |
| new_st.op = EXEC_IF; |
| new_st.expr1 = expr; |
| return MATCH_YES; |
| |
| cleanup: |
| gfc_free_expr (expr); |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Free a gfc_iterator structure. */ |
| |
| void |
| gfc_free_iterator (gfc_iterator *iter, int flag) |
| { |
| |
| if (iter == NULL) |
| return; |
| |
| gfc_free_expr (iter->var); |
| gfc_free_expr (iter->start); |
| gfc_free_expr (iter->end); |
| gfc_free_expr (iter->step); |
| |
| if (flag) |
| free (iter); |
| } |
| |
| |
| /* Match a CRITICAL statement. */ |
| match |
| gfc_match_critical (void) |
| { |
| gfc_st_label *label = NULL; |
| |
| if (gfc_match_label () == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| if (gfc_match (" critical") != MATCH_YES) |
| return MATCH_NO; |
| |
| if (gfc_match_st_label (&label) == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| gfc_syntax_error (ST_CRITICAL); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_pure (NULL)) |
| { |
| gfc_error ("Image control statement CRITICAL at %C in PURE procedure"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_DO_CONCURRENT) == SUCCESS) |
| { |
| gfc_error ("Image control statement CRITICAL at %C in DO CONCURRENT " |
| "block"); |
| return MATCH_ERROR; |
| } |
| |
| gfc_unset_implicit_pure (NULL); |
| |
| if (gfc_notify_std (GFC_STD_F2008, "CRITICAL statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| if (gfc_option.coarray == GFC_FCOARRAY_NONE) |
| { |
| gfc_fatal_error ("Coarrays disabled at %C, use -fcoarray= to enable"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_CRITICAL) == SUCCESS) |
| { |
| gfc_error ("Nested CRITICAL block at %C"); |
| return MATCH_ERROR; |
| } |
| |
| new_st.op = EXEC_CRITICAL; |
| |
| if (label != NULL |
| && gfc_reference_st_label (label, ST_LABEL_TARGET) == FAILURE) |
| return MATCH_ERROR; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match a BLOCK statement. */ |
| |
| match |
| gfc_match_block (void) |
| { |
| match m; |
| |
| if (gfc_match_label () == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| if (gfc_match (" block") != MATCH_YES) |
| return MATCH_NO; |
| |
| /* For this to be a correct BLOCK statement, the line must end now. */ |
| m = gfc_match_eos (); |
| if (m == MATCH_ERROR) |
| return MATCH_ERROR; |
| if (m == MATCH_NO) |
| return MATCH_NO; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match an ASSOCIATE statement. */ |
| |
| match |
| gfc_match_associate (void) |
| { |
| if (gfc_match_label () == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| if (gfc_match (" associate") != MATCH_YES) |
| return MATCH_NO; |
| |
| /* Match the association list. */ |
| if (gfc_match_char ('(') != MATCH_YES) |
| { |
| gfc_error ("Expected association list at %C"); |
| return MATCH_ERROR; |
| } |
| new_st.ext.block.assoc = NULL; |
| while (true) |
| { |
| gfc_association_list* newAssoc = gfc_get_association_list (); |
| gfc_association_list* a; |
| |
| /* Match the next association. */ |
| if (gfc_match (" %n => %e", newAssoc->name, &newAssoc->target) |
| != MATCH_YES) |
| { |
| gfc_error ("Expected association at %C"); |
| goto assocListError; |
| } |
| newAssoc->where = gfc_current_locus; |
| |
| /* Check that the current name is not yet in the list. */ |
| for (a = new_st.ext.block.assoc; a; a = a->next) |
| if (!strcmp (a->name, newAssoc->name)) |
| { |
| gfc_error ("Duplicate name '%s' in association at %C", |
| newAssoc->name); |
| goto assocListError; |
| } |
| |
| /* The target expression must not be coindexed. */ |
| if (gfc_is_coindexed (newAssoc->target)) |
| { |
| gfc_error ("Association target at %C must not be coindexed"); |
| goto assocListError; |
| } |
| |
| /* The `variable' field is left blank for now; because the target is not |
| yet resolved, we can't use gfc_has_vector_subscript to determine it |
| for now. This is set during resolution. */ |
| |
| /* Put it into the list. */ |
| newAssoc->next = new_st.ext.block.assoc; |
| new_st.ext.block.assoc = newAssoc; |
| |
| /* Try next one or end if closing parenthesis is found. */ |
| gfc_gobble_whitespace (); |
| if (gfc_peek_char () == ')') |
| break; |
| if (gfc_match_char (',') != MATCH_YES) |
| { |
| gfc_error ("Expected ')' or ',' at %C"); |
| return MATCH_ERROR; |
| } |
| |
| continue; |
| |
| assocListError: |
| free (newAssoc); |
| goto error; |
| } |
| if (gfc_match_char (')') != MATCH_YES) |
| { |
| /* This should never happen as we peek above. */ |
| gcc_unreachable (); |
| } |
| |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| gfc_error ("Junk after ASSOCIATE statement at %C"); |
| goto error; |
| } |
| |
| return MATCH_YES; |
| |
| error: |
| gfc_free_association_list (new_st.ext.block.assoc); |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match a Fortran 2003 derived-type-spec (F03:R455), which is just the name of |
| an accessible derived type. */ |
| |
| static match |
| match_derived_type_spec (gfc_typespec *ts) |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| locus old_locus; |
| gfc_symbol *derived; |
| |
| old_locus = gfc_current_locus; |
| |
| if (gfc_match ("%n", name) != MATCH_YES) |
| { |
| gfc_current_locus = old_locus; |
| return MATCH_NO; |
| } |
| |
| gfc_find_symbol (name, NULL, 1, &derived); |
| |
| if (derived && derived->attr.flavor == FL_PROCEDURE && derived->attr.generic) |
| derived = gfc_find_dt_in_generic (derived); |
| |
| if (derived && derived->attr.flavor == FL_DERIVED) |
| { |
| ts->type = BT_DERIVED; |
| ts->u.derived = derived; |
| return MATCH_YES; |
| } |
| |
| gfc_current_locus = old_locus; |
| return MATCH_NO; |
| } |
| |
| |
| /* Match a Fortran 2003 type-spec (F03:R401). This is similar to |
| gfc_match_decl_type_spec() from decl.c, with the following exceptions: |
| It only includes the intrinsic types from the Fortran 2003 standard |
| (thus, neither BYTE nor forms like REAL*4 are allowed). Additionally, |
| the implicit_flag is not needed, so it was removed. Derived types are |
| identified by their name alone. */ |
| |
| static match |
| match_type_spec (gfc_typespec *ts) |
| { |
| match m; |
| locus old_locus; |
| |
| gfc_clear_ts (ts); |
| gfc_gobble_whitespace (); |
| old_locus = gfc_current_locus; |
| |
| if (match_derived_type_spec (ts) == MATCH_YES) |
| { |
| /* Enforce F03:C401. */ |
| if (ts->u.derived->attr.abstract) |
| { |
| gfc_error ("Derived type '%s' at %L may not be ABSTRACT", |
| ts->u.derived->name, &old_locus); |
| return MATCH_ERROR; |
| } |
| return MATCH_YES; |
| } |
| |
| if (gfc_match ("integer") == MATCH_YES) |
| { |
| ts->type = BT_INTEGER; |
| ts->kind = gfc_default_integer_kind; |
| goto kind_selector; |
| } |
| |
| if (gfc_match ("real") == MATCH_YES) |
| { |
| ts->type = BT_REAL; |
| ts->kind = gfc_default_real_kind; |
| goto kind_selector; |
| } |
| |
| if (gfc_match ("double precision") == MATCH_YES) |
| { |
| ts->type = BT_REAL; |
| ts->kind = gfc_default_double_kind; |
| return MATCH_YES; |
| } |
| |
| if (gfc_match ("complex") == MATCH_YES) |
| { |
| ts->type = BT_COMPLEX; |
| ts->kind = gfc_default_complex_kind; |
| goto kind_selector; |
| } |
| |
| if (gfc_match ("character") == MATCH_YES) |
| { |
| ts->type = BT_CHARACTER; |
| |
| m = gfc_match_char_spec (ts); |
| |
| if (m == MATCH_NO) |
| m = MATCH_YES; |
| |
| return m; |
| } |
| |
| if (gfc_match ("logical") == MATCH_YES) |
| { |
| ts->type = BT_LOGICAL; |
| ts->kind = gfc_default_logical_kind; |
| goto kind_selector; |
| } |
| |
| /* If a type is not matched, simply return MATCH_NO. */ |
| gfc_current_locus = old_locus; |
| return MATCH_NO; |
| |
| kind_selector: |
| |
| gfc_gobble_whitespace (); |
| if (gfc_peek_ascii_char () == '*') |
| { |
| gfc_error ("Invalid type-spec at %C"); |
| return MATCH_ERROR; |
| } |
| |
| m = gfc_match_kind_spec (ts, false); |
| |
| if (m == MATCH_NO) |
| m = MATCH_YES; /* No kind specifier found. */ |
| |
| return m; |
| } |
| |
| |
| /******************** FORALL subroutines ********************/ |
| |
| /* Free a list of FORALL iterators. */ |
| |
| void |
| gfc_free_forall_iterator (gfc_forall_iterator *iter) |
| { |
| gfc_forall_iterator *next; |
| |
| while (iter) |
| { |
| next = iter->next; |
| gfc_free_expr (iter->var); |
| gfc_free_expr (iter->start); |
| gfc_free_expr (iter->end); |
| gfc_free_expr (iter->stride); |
| free (iter); |
| iter = next; |
| } |
| } |
| |
| |
| /* Match an iterator as part of a FORALL statement. The format is: |
| |
| <var> = <start>:<end>[:<stride>] |
| |
| On MATCH_NO, the caller tests for the possibility that there is a |
| scalar mask expression. */ |
| |
| static match |
| match_forall_iterator (gfc_forall_iterator **result) |
| { |
| gfc_forall_iterator *iter; |
| locus where; |
| match m; |
| |
| where = gfc_current_locus; |
| iter = XCNEW (gfc_forall_iterator); |
| |
| m = gfc_match_expr (&iter->var); |
| if (m != MATCH_YES) |
| goto cleanup; |
| |
| if (gfc_match_char ('=') != MATCH_YES |
| || iter->var->expr_type != EXPR_VARIABLE) |
| { |
| m = MATCH_NO; |
| goto cleanup; |
| } |
| |
| m = gfc_match_expr (&iter->start); |
| if (m != MATCH_YES) |
| goto cleanup; |
| |
| if (gfc_match_char (':') != MATCH_YES) |
| goto syntax; |
| |
| m = gfc_match_expr (&iter->end); |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| if (gfc_match_char (':') == MATCH_NO) |
| iter->stride = gfc_get_int_expr (gfc_default_integer_kind, NULL, 1); |
| else |
| { |
| m = gfc_match_expr (&iter->stride); |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| } |
| |
| /* Mark the iteration variable's symbol as used as a FORALL index. */ |
| iter->var->symtree->n.sym->forall_index = true; |
| |
| *result = iter; |
| return MATCH_YES; |
| |
| syntax: |
| gfc_error ("Syntax error in FORALL iterator at %C"); |
| m = MATCH_ERROR; |
| |
| cleanup: |
| |
| gfc_current_locus = where; |
| gfc_free_forall_iterator (iter); |
| return m; |
| } |
| |
| |
| /* Match the header of a FORALL statement. */ |
| |
| static match |
| match_forall_header (gfc_forall_iterator **phead, gfc_expr **mask) |
| { |
| gfc_forall_iterator *head, *tail, *new_iter; |
| gfc_expr *msk; |
| match m; |
| |
| gfc_gobble_whitespace (); |
| |
| head = tail = NULL; |
| msk = NULL; |
| |
| if (gfc_match_char ('(') != MATCH_YES) |
| return MATCH_NO; |
| |
| m = match_forall_iterator (&new_iter); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| goto syntax; |
| |
| head = tail = new_iter; |
| |
| for (;;) |
| { |
| if (gfc_match_char (',') != MATCH_YES) |
| break; |
| |
| m = match_forall_iterator (&new_iter); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| if (m == MATCH_YES) |
| { |
| tail->next = new_iter; |
| tail = new_iter; |
| continue; |
| } |
| |
| /* Have to have a mask expression. */ |
| |
| m = gfc_match_expr (&msk); |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| break; |
| } |
| |
| if (gfc_match_char (')') == MATCH_NO) |
| goto syntax; |
| |
| *phead = head; |
| *mask = msk; |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (ST_FORALL); |
| |
| cleanup: |
| gfc_free_expr (msk); |
| gfc_free_forall_iterator (head); |
| |
| return MATCH_ERROR; |
| } |
| |
| /* Match the rest of a simple FORALL statement that follows an |
| IF statement. */ |
| |
| static match |
| match_simple_forall (void) |
| { |
| gfc_forall_iterator *head; |
| gfc_expr *mask; |
| gfc_code *c; |
| match m; |
| |
| mask = NULL; |
| head = NULL; |
| c = NULL; |
| |
| m = match_forall_header (&head, &mask); |
| |
| if (m == MATCH_NO) |
| goto syntax; |
| if (m != MATCH_YES) |
| goto cleanup; |
| |
| m = gfc_match_assignment (); |
| |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| { |
| m = gfc_match_pointer_assignment (); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| goto syntax; |
| } |
| |
| c = gfc_get_code (); |
| *c = new_st; |
| c->loc = gfc_current_locus; |
| |
| if (gfc_match_eos () != MATCH_YES) |
| goto syntax; |
| |
| gfc_clear_new_st (); |
| new_st.op = EXEC_FORALL; |
| new_st.expr1 = mask; |
| new_st.ext.forall_iterator = head; |
| new_st.block = gfc_get_code (); |
| |
| new_st.block->op = EXEC_FORALL; |
| new_st.block->next = c; |
| |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (ST_FORALL); |
| |
| cleanup: |
| gfc_free_forall_iterator (head); |
| gfc_free_expr (mask); |
| |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match a FORALL statement. */ |
| |
| match |
| gfc_match_forall (gfc_statement *st) |
| { |
| gfc_forall_iterator *head; |
| gfc_expr *mask; |
| gfc_code *c; |
| match m0, m; |
| |
| head = NULL; |
| mask = NULL; |
| c = NULL; |
| |
| m0 = gfc_match_label (); |
| if (m0 == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| m = gfc_match (" forall"); |
| if (m != MATCH_YES) |
| return m; |
| |
| m = match_forall_header (&head, &mask); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| goto syntax; |
| |
| if (gfc_match_eos () == MATCH_YES) |
| { |
| *st = ST_FORALL_BLOCK; |
| new_st.op = EXEC_FORALL; |
| new_st.expr1 = mask; |
| new_st.ext.forall_iterator = head; |
| return MATCH_YES; |
| } |
| |
| m = gfc_match_assignment (); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| { |
| m = gfc_match_pointer_assignment (); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| goto syntax; |
| } |
| |
| c = gfc_get_code (); |
| *c = new_st; |
| c->loc = gfc_current_locus; |
| |
| gfc_clear_new_st (); |
| new_st.op = EXEC_FORALL; |
| new_st.expr1 = mask; |
| new_st.ext.forall_iterator = head; |
| new_st.block = gfc_get_code (); |
| new_st.block->op = EXEC_FORALL; |
| new_st.block->next = c; |
| |
| *st = ST_FORALL; |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (ST_FORALL); |
| |
| cleanup: |
| gfc_free_forall_iterator (head); |
| gfc_free_expr (mask); |
| gfc_free_statements (c); |
| return MATCH_NO; |
| } |
| |
| |
| /* Match a DO statement. */ |
| |
| match |
| gfc_match_do (void) |
| { |
| gfc_iterator iter, *ip; |
| locus old_loc; |
| gfc_st_label *label; |
| match m; |
| |
| old_loc = gfc_current_locus; |
| |
| label = NULL; |
| iter.var = iter.start = iter.end = iter.step = NULL; |
| |
| m = gfc_match_label (); |
| if (m == MATCH_ERROR) |
| return m; |
| |
| if (gfc_match (" do") != MATCH_YES) |
| return MATCH_NO; |
| |
| m = gfc_match_st_label (&label); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| /* Match an infinite DO, make it like a DO WHILE(.TRUE.). */ |
| |
| if (gfc_match_eos () == MATCH_YES) |
| { |
| iter.end = gfc_get_logical_expr (gfc_default_logical_kind, NULL, true); |
| new_st.op = EXEC_DO_WHILE; |
| goto done; |
| } |
| |
| /* Match an optional comma, if no comma is found, a space is obligatory. */ |
| if (gfc_match_char (',') != MATCH_YES && gfc_match ("% ") != MATCH_YES) |
| return MATCH_NO; |
| |
| /* Check for balanced parens. */ |
| |
| if (gfc_match_parens () == MATCH_ERROR) |
| return MATCH_ERROR; |
| |
| if (gfc_match (" concurrent") == MATCH_YES) |
| { |
| gfc_forall_iterator *head; |
| gfc_expr *mask; |
| |
| if (gfc_notify_std (GFC_STD_F2008, "DO CONCURRENT " |
| "construct at %C") == FAILURE) |
| return MATCH_ERROR; |
| |
| |
| mask = NULL; |
| head = NULL; |
| m = match_forall_header (&head, &mask); |
| |
| if (m == MATCH_NO) |
| return m; |
| if (m == MATCH_ERROR) |
| goto concurr_cleanup; |
| |
| if (gfc_match_eos () != MATCH_YES) |
| goto concurr_cleanup; |
| |
| if (label != NULL |
| && gfc_reference_st_label (label, ST_LABEL_DO_TARGET) == FAILURE) |
| goto concurr_cleanup; |
| |
| new_st.label1 = label; |
| new_st.op = EXEC_DO_CONCURRENT; |
| new_st.expr1 = mask; |
| new_st.ext.forall_iterator = head; |
| |
| return MATCH_YES; |
| |
| concurr_cleanup: |
| gfc_syntax_error (ST_DO); |
| gfc_free_expr (mask); |
| gfc_free_forall_iterator (head); |
| return MATCH_ERROR; |
| } |
| |
| /* See if we have a DO WHILE. */ |
| if (gfc_match (" while ( %e )%t", &iter.end) == MATCH_YES) |
| { |
| new_st.op = EXEC_DO_WHILE; |
| goto done; |
| } |
| |
| /* The abortive DO WHILE may have done something to the symbol |
| table, so we start over. */ |
| gfc_undo_symbols (); |
| gfc_current_locus = old_loc; |
| |
| gfc_match_label (); /* This won't error. */ |
| gfc_match (" do "); /* This will work. */ |
| |
| gfc_match_st_label (&label); /* Can't error out. */ |
| gfc_match_char (','); /* Optional comma. */ |
| |
| m = gfc_match_iterator (&iter, 0); |
| if (m == MATCH_NO) |
| return MATCH_NO; |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| |
| iter.var->symtree->n.sym->attr.implied_index = 0; |
| gfc_check_do_variable (iter.var->symtree); |
| |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| gfc_syntax_error (ST_DO); |
| goto cleanup; |
| } |
| |
| new_st.op = EXEC_DO; |
| |
| done: |
| if (label != NULL |
| && gfc_reference_st_label (label, ST_LABEL_DO_TARGET) == FAILURE) |
| goto cleanup; |
| |
| new_st.label1 = label; |
| |
| if (new_st.op == EXEC_DO_WHILE) |
| new_st.expr1 = iter.end; |
| else |
| { |
| new_st.ext.iterator = ip = gfc_get_iterator (); |
| *ip = iter; |
| } |
| |
| return MATCH_YES; |
| |
| cleanup: |
| gfc_free_iterator (&iter, 0); |
| |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match an EXIT or CYCLE statement. */ |
| |
| static match |
| match_exit_cycle (gfc_statement st, gfc_exec_op op) |
| { |
| gfc_state_data *p, *o; |
| gfc_symbol *sym; |
| match m; |
| int cnt; |
| |
| if (gfc_match_eos () == MATCH_YES) |
| sym = NULL; |
| else |
| { |
| char name[GFC_MAX_SYMBOL_LEN + 1]; |
| gfc_symtree* stree; |
| |
| m = gfc_match ("% %n%t", name); |
| if (m == MATCH_ERROR) |
| return MATCH_ERROR; |
| if (m == MATCH_NO) |
| { |
| gfc_syntax_error (st); |
| return MATCH_ERROR; |
| } |
| |
| /* Find the corresponding symbol. If there's a BLOCK statement |
| between here and the label, it is not in gfc_current_ns but a parent |
| namespace! */ |
| stree = gfc_find_symtree_in_proc (name, gfc_current_ns); |
| if (!stree) |
| { |
| gfc_error ("Name '%s' in %s statement at %C is unknown", |
| name, gfc_ascii_statement (st)); |
| return MATCH_ERROR; |
| } |
| |
| sym = stree->n.sym; |
| if (sym->attr.flavor != FL_LABEL) |
| { |
| gfc_error ("Name '%s' in %s statement at %C is not a construct name", |
| name, gfc_ascii_statement (st)); |
| return MATCH_ERROR; |
| } |
| } |
| |
| /* Find the loop specified by the label (or lack of a label). */ |
| for (o = NULL, p = gfc_state_stack; p; p = p->previous) |
| if (o == NULL && p->state == COMP_OMP_STRUCTURED_BLOCK) |
| o = p; |
| else if (p->state == COMP_CRITICAL) |
| { |
| gfc_error("%s statement at %C leaves CRITICAL construct", |
| gfc_ascii_statement (st)); |
| return MATCH_ERROR; |
| } |
| else if (p->state == COMP_DO_CONCURRENT |
| && (op == EXEC_EXIT || (sym && sym != p->sym))) |
| { |
| /* F2008, C821 & C845. */ |
| gfc_error("%s statement at %C leaves DO CONCURRENT construct", |
| gfc_ascii_statement (st)); |
| return MATCH_ERROR; |
| } |
| else if ((sym && sym == p->sym) |
| || (!sym && (p->state == COMP_DO |
| || p->state == COMP_DO_CONCURRENT))) |
| break; |
| |
| if (p == NULL) |
| { |
| if (sym == NULL) |
| gfc_error ("%s statement at %C is not within a construct", |
| gfc_ascii_statement (st)); |
| else |
| gfc_error ("%s statement at %C is not within construct '%s'", |
| gfc_ascii_statement (st), sym->name); |
| |
| return MATCH_ERROR; |
| } |
| |
| /* Special checks for EXIT from non-loop constructs. */ |
| switch (p->state) |
| { |
| case COMP_DO: |
| case COMP_DO_CONCURRENT: |
| break; |
| |
| case COMP_CRITICAL: |
| /* This is already handled above. */ |
| gcc_unreachable (); |
| |
| case COMP_ASSOCIATE: |
| case COMP_BLOCK: |
| case COMP_IF: |
| case COMP_SELECT: |
| case COMP_SELECT_TYPE: |
| gcc_assert (sym); |
| if (op == EXEC_CYCLE) |
| { |
| gfc_error ("CYCLE statement at %C is not applicable to non-loop" |
| " construct '%s'", sym->name); |
| return MATCH_ERROR; |
| } |
| gcc_assert (op == EXEC_EXIT); |
| if (gfc_notify_std (GFC_STD_F2008, "EXIT statement with no" |
| " do-construct-name at %C") == FAILURE) |
| return MATCH_ERROR; |
| break; |
| |
| default: |
| gfc_error ("%s statement at %C is not applicable to construct '%s'", |
| gfc_ascii_statement (st), sym->name); |
| return MATCH_ERROR; |
| } |
| |
| if (o != NULL) |
| { |
| gfc_error ("%s statement at %C leaving OpenMP structured block", |
| gfc_ascii_statement (st)); |
| return MATCH_ERROR; |
| } |
| |
| for (o = p, cnt = 0; o->state == COMP_DO && o->previous != NULL; cnt++) |
| o = o->previous; |
| if (cnt > 0 |
| && o != NULL |
| && o->state == COMP_OMP_STRUCTURED_BLOCK |
| && (o->head->op == EXEC_OMP_DO |
| || o->head->op == EXEC_OMP_PARALLEL_DO)) |
| { |
| int collapse = 1; |
| gcc_assert (o->head->next != NULL |
| && (o->head->next->op == EXEC_DO |
| || o->head->next->op == EXEC_DO_WHILE) |
| && o->previous != NULL |
| && o->previous->tail->op == o->head->op); |
| if (o->previous->tail->ext.omp_clauses != NULL |
| && o->previous->tail->ext.omp_clauses->collapse > 1) |
| collapse = o->previous->tail->ext.omp_clauses->collapse; |
| if (st == ST_EXIT && cnt <= collapse) |
| { |
| gfc_error ("EXIT statement at %C terminating !$OMP DO loop"); |
| return MATCH_ERROR; |
| } |
| if (st == ST_CYCLE && cnt < collapse) |
| { |
| gfc_error ("CYCLE statement at %C to non-innermost collapsed" |
| " !$OMP DO loop"); |
| return MATCH_ERROR; |
| } |
| } |
| |
| /* Save the first statement in the construct - needed by the backend. */ |
| new_st.ext.which_construct = p->construct; |
| |
| new_st.op = op; |
| |
| return MATCH_YES; |
| } |
| |
| |
| /* Match the EXIT statement. */ |
| |
| match |
| gfc_match_exit (void) |
| { |
| return match_exit_cycle (ST_EXIT, EXEC_EXIT); |
| } |
| |
| |
| /* Match the CYCLE statement. */ |
| |
| match |
| gfc_match_cycle (void) |
| { |
| return match_exit_cycle (ST_CYCLE, EXEC_CYCLE); |
| } |
| |
| |
| /* Match a number or character constant after an (ALL) STOP or PAUSE statement. */ |
| |
| static match |
| gfc_match_stopcode (gfc_statement st) |
| { |
| gfc_expr *e; |
| match m; |
| |
| e = NULL; |
| |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| m = gfc_match_init_expr (&e); |
| if (m == MATCH_ERROR) |
| goto cleanup; |
| if (m == MATCH_NO) |
| goto syntax; |
| |
| if (gfc_match_eos () != MATCH_YES) |
| goto syntax; |
| } |
| |
| if (gfc_pure (NULL)) |
| { |
| gfc_error ("%s statement not allowed in PURE procedure at %C", |
| gfc_ascii_statement (st)); |
| goto cleanup; |
| } |
| |
| gfc_unset_implicit_pure (NULL); |
| |
| if (st == ST_STOP && gfc_find_state (COMP_CRITICAL) == SUCCESS) |
| { |
| gfc_error ("Image control statement STOP at %C in CRITICAL block"); |
| goto cleanup; |
| } |
| if (st == ST_STOP && gfc_find_state (COMP_DO_CONCURRENT) == SUCCESS) |
| { |
| gfc_error ("Image control statement STOP at %C in DO CONCURRENT block"); |
| goto cleanup; |
| } |
| |
| if (e != NULL) |
| { |
| if (!(e->ts.type == BT_CHARACTER || e->ts.type == BT_INTEGER)) |
| { |
| gfc_error ("STOP code at %L must be either INTEGER or CHARACTER type", |
| &e->where); |
| goto cleanup; |
| } |
| |
| if (e->rank != 0) |
| { |
| gfc_error ("STOP code at %L must be scalar", |
| &e->where); |
| goto cleanup; |
| } |
| |
| if (e->ts.type == BT_CHARACTER |
| && e->ts.kind != gfc_default_character_kind) |
| { |
| gfc_error ("STOP code at %L must be default character KIND=%d", |
| &e->where, (int) gfc_default_character_kind); |
| goto cleanup; |
| } |
| |
| if (e->ts.type == BT_INTEGER |
| && e->ts.kind != gfc_default_integer_kind) |
| { |
| gfc_error ("STOP code at %L must be default integer KIND=%d", |
| &e->where, (int) gfc_default_integer_kind); |
| goto cleanup; |
| } |
| } |
| |
| switch (st) |
| { |
| case ST_STOP: |
| new_st.op = EXEC_STOP; |
| break; |
| case ST_ERROR_STOP: |
| new_st.op = EXEC_ERROR_STOP; |
| break; |
| case ST_PAUSE: |
| new_st.op = EXEC_PAUSE; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| new_st.expr1 = e; |
| new_st.ext.stop_code = -1; |
| |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (st); |
| |
| cleanup: |
| |
| gfc_free_expr (e); |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match the (deprecated) PAUSE statement. */ |
| |
| match |
| gfc_match_pause (void) |
| { |
| match m; |
| |
| m = gfc_match_stopcode (ST_PAUSE); |
| if (m == MATCH_YES) |
| { |
| if (gfc_notify_std (GFC_STD_F95_DEL, "PAUSE statement" |
| " at %C") |
| == FAILURE) |
| m = MATCH_ERROR; |
| } |
| return m; |
| } |
| |
| |
| /* Match the STOP statement. */ |
| |
| match |
| gfc_match_stop (void) |
| { |
| return gfc_match_stopcode (ST_STOP); |
| } |
| |
| |
| /* Match the ERROR STOP statement. */ |
| |
| match |
| gfc_match_error_stop (void) |
| { |
| if (gfc_notify_std (GFC_STD_F2008, "ERROR STOP statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| return gfc_match_stopcode (ST_ERROR_STOP); |
| } |
| |
| |
| /* Match LOCK/UNLOCK statement. Syntax: |
| LOCK ( lock-variable [ , lock-stat-list ] ) |
| UNLOCK ( lock-variable [ , sync-stat-list ] ) |
| where lock-stat is ACQUIRED_LOCK or sync-stat |
| and sync-stat is STAT= or ERRMSG=. */ |
| |
| static match |
| lock_unlock_statement (gfc_statement st) |
| { |
| match m; |
| gfc_expr *tmp, *lockvar, *acq_lock, *stat, *errmsg; |
| bool saw_acq_lock, saw_stat, saw_errmsg; |
| |
| tmp = lockvar = acq_lock = stat = errmsg = NULL; |
| saw_acq_lock = saw_stat = saw_errmsg = false; |
| |
| if (gfc_pure (NULL)) |
| { |
| gfc_error ("Image control statement %s at %C in PURE procedure", |
| st == ST_LOCK ? "LOCK" : "UNLOCK"); |
| return MATCH_ERROR; |
| } |
| |
| gfc_unset_implicit_pure (NULL); |
| |
| if (gfc_option.coarray == GFC_FCOARRAY_NONE) |
| { |
| gfc_fatal_error ("Coarrays disabled at %C, use -fcoarray= to enable"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_CRITICAL) == SUCCESS) |
| { |
| gfc_error ("Image control statement %s at %C in CRITICAL block", |
| st == ST_LOCK ? "LOCK" : "UNLOCK"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_DO_CONCURRENT) == SUCCESS) |
| { |
| gfc_error ("Image control statement %s at %C in DO CONCURRENT block", |
| st == ST_LOCK ? "LOCK" : "UNLOCK"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_match_char ('(') != MATCH_YES) |
| goto syntax; |
| |
| if (gfc_match ("%e", &lockvar) != MATCH_YES) |
| goto syntax; |
| m = gfc_match_char (','); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_NO) |
| { |
| m = gfc_match_char (')'); |
| if (m == MATCH_YES) |
| goto done; |
| goto syntax; |
| } |
| |
| for (;;) |
| { |
| m = gfc_match (" stat = %v", &tmp); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_YES) |
| { |
| if (saw_stat) |
| { |
| gfc_error ("Redundant STAT tag found at %L ", &tmp->where); |
| goto cleanup; |
| } |
| stat = tmp; |
| saw_stat = true; |
| |
| m = gfc_match_char (','); |
| if (m == MATCH_YES) |
| continue; |
| |
| tmp = NULL; |
| break; |
| } |
| |
| m = gfc_match (" errmsg = %v", &tmp); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_YES) |
| { |
| if (saw_errmsg) |
| { |
| gfc_error ("Redundant ERRMSG tag found at %L ", &tmp->where); |
| goto cleanup; |
| } |
| errmsg = tmp; |
| saw_errmsg = true; |
| |
| m = gfc_match_char (','); |
| if (m == MATCH_YES) |
| continue; |
| |
| tmp = NULL; |
| break; |
| } |
| |
| m = gfc_match (" acquired_lock = %v", &tmp); |
| if (m == MATCH_ERROR || st == ST_UNLOCK) |
| goto syntax; |
| if (m == MATCH_YES) |
| { |
| if (saw_acq_lock) |
| { |
| gfc_error ("Redundant ACQUIRED_LOCK tag found at %L ", |
| &tmp->where); |
| goto cleanup; |
| } |
| acq_lock = tmp; |
| saw_acq_lock = true; |
| |
| m = gfc_match_char (','); |
| if (m == MATCH_YES) |
| continue; |
| |
| tmp = NULL; |
| break; |
| } |
| |
| break; |
| } |
| |
| if (m == MATCH_ERROR) |
| goto syntax; |
| |
| if (gfc_match (" )%t") != MATCH_YES) |
| goto syntax; |
| |
| done: |
| switch (st) |
| { |
| case ST_LOCK: |
| new_st.op = EXEC_LOCK; |
| break; |
| case ST_UNLOCK: |
| new_st.op = EXEC_UNLOCK; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| new_st.expr1 = lockvar; |
| new_st.expr2 = stat; |
| new_st.expr3 = errmsg; |
| new_st.expr4 = acq_lock; |
| |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (st); |
| |
| cleanup: |
| if (acq_lock != tmp) |
| gfc_free_expr (acq_lock); |
| if (errmsg != tmp) |
| gfc_free_expr (errmsg); |
| if (stat != tmp) |
| gfc_free_expr (stat); |
| |
| gfc_free_expr (tmp); |
| gfc_free_expr (lockvar); |
| |
| return MATCH_ERROR; |
| } |
| |
| |
| match |
| gfc_match_lock (void) |
| { |
| if (gfc_notify_std (GFC_STD_F2008, "LOCK statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| return lock_unlock_statement (ST_LOCK); |
| } |
| |
| |
| match |
| gfc_match_unlock (void) |
| { |
| if (gfc_notify_std (GFC_STD_F2008, "UNLOCK statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| return lock_unlock_statement (ST_UNLOCK); |
| } |
| |
| |
| /* Match SYNC ALL/IMAGES/MEMORY statement. Syntax: |
| SYNC ALL [(sync-stat-list)] |
| SYNC MEMORY [(sync-stat-list)] |
| SYNC IMAGES (image-set [, sync-stat-list] ) |
| with sync-stat is int-expr or *. */ |
| |
| static match |
| sync_statement (gfc_statement st) |
| { |
| match m; |
| gfc_expr *tmp, *imageset, *stat, *errmsg; |
| bool saw_stat, saw_errmsg; |
| |
| tmp = imageset = stat = errmsg = NULL; |
| saw_stat = saw_errmsg = false; |
| |
| if (gfc_pure (NULL)) |
| { |
| gfc_error ("Image control statement SYNC at %C in PURE procedure"); |
| return MATCH_ERROR; |
| } |
| |
| gfc_unset_implicit_pure (NULL); |
| |
| if (gfc_notify_std (GFC_STD_F2008, "SYNC statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| if (gfc_option.coarray == GFC_FCOARRAY_NONE) |
| { |
| gfc_fatal_error ("Coarrays disabled at %C, use -fcoarray= to enable"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_CRITICAL) == SUCCESS) |
| { |
| gfc_error ("Image control statement SYNC at %C in CRITICAL block"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_find_state (COMP_DO_CONCURRENT) == SUCCESS) |
| { |
| gfc_error ("Image control statement SYNC at %C in DO CONCURRENT block"); |
| return MATCH_ERROR; |
| } |
| |
| if (gfc_match_eos () == MATCH_YES) |
| { |
| if (st == ST_SYNC_IMAGES) |
| goto syntax; |
| goto done; |
| } |
| |
| if (gfc_match_char ('(') != MATCH_YES) |
| goto syntax; |
| |
| if (st == ST_SYNC_IMAGES) |
| { |
| /* Denote '*' as imageset == NULL. */ |
| m = gfc_match_char ('*'); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_NO) |
| { |
| if (gfc_match ("%e", &imageset) != MATCH_YES) |
| goto syntax; |
| } |
| m = gfc_match_char (','); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_NO) |
| { |
| m = gfc_match_char (')'); |
| if (m == MATCH_YES) |
| goto done; |
| goto syntax; |
| } |
| } |
| |
| for (;;) |
| { |
| m = gfc_match (" stat = %v", &tmp); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_YES) |
| { |
| if (saw_stat) |
| { |
| gfc_error ("Redundant STAT tag found at %L ", &tmp->where); |
| goto cleanup; |
| } |
| stat = tmp; |
| saw_stat = true; |
| |
| if (gfc_match_char (',') == MATCH_YES) |
| continue; |
| |
| tmp = NULL; |
| break; |
| } |
| |
| m = gfc_match (" errmsg = %v", &tmp); |
| if (m == MATCH_ERROR) |
| goto syntax; |
| if (m == MATCH_YES) |
| { |
| if (saw_errmsg) |
| { |
| gfc_error ("Redundant ERRMSG tag found at %L ", &tmp->where); |
| goto cleanup; |
| } |
| errmsg = tmp; |
| saw_errmsg = true; |
| |
| if (gfc_match_char (',') == MATCH_YES) |
| continue; |
| |
| tmp = NULL; |
| break; |
| } |
| |
| break; |
| } |
| |
| if (gfc_match (" )%t") != MATCH_YES) |
| goto syntax; |
| |
| done: |
| switch (st) |
| { |
| case ST_SYNC_ALL: |
| new_st.op = EXEC_SYNC_ALL; |
| break; |
| case ST_SYNC_IMAGES: |
| new_st.op = EXEC_SYNC_IMAGES; |
| break; |
| case ST_SYNC_MEMORY: |
| new_st.op = EXEC_SYNC_MEMORY; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| new_st.expr1 = imageset; |
| new_st.expr2 = stat; |
| new_st.expr3 = errmsg; |
| |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (st); |
| |
| cleanup: |
| if (stat != tmp) |
| gfc_free_expr (stat); |
| if (errmsg != tmp) |
| gfc_free_expr (errmsg); |
| |
| gfc_free_expr (tmp); |
| gfc_free_expr (imageset); |
| |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Match SYNC ALL statement. */ |
| |
| match |
| gfc_match_sync_all (void) |
| { |
| return sync_statement (ST_SYNC_ALL); |
| } |
| |
| |
| /* Match SYNC IMAGES statement. */ |
| |
| match |
| gfc_match_sync_images (void) |
| { |
| return sync_statement (ST_SYNC_IMAGES); |
| } |
| |
| |
| /* Match SYNC MEMORY statement. */ |
| |
| match |
| gfc_match_sync_memory (void) |
| { |
| return sync_statement (ST_SYNC_MEMORY); |
| } |
| |
| |
| /* Match a CONTINUE statement. */ |
| |
| match |
| gfc_match_continue (void) |
| { |
| if (gfc_match_eos () != MATCH_YES) |
| { |
| gfc_syntax_error (ST_CONTINUE); |
| return MATCH_ERROR; |
| } |
| |
| new_st.op = EXEC_CONTINUE; |
| return MATCH_YES; |
| } |
| |
| |
| /* Match the (deprecated) ASSIGN statement. */ |
| |
| match |
| gfc_match_assign (void) |
| { |
| gfc_expr *expr; |
| gfc_st_label *label; |
| |
| if (gfc_match (" %l", &label) == MATCH_YES) |
| { |
| if (gfc_reference_st_label (label, ST_LABEL_UNKNOWN) == FAILURE) |
| return MATCH_ERROR; |
| if (gfc_match (" to %v%t", &expr) == MATCH_YES) |
| { |
| if (gfc_notify_std (GFC_STD_F95_DEL, "ASSIGN " |
| "statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| expr->symtree->n.sym->attr.assign = 1; |
| |
| new_st.op = EXEC_LABEL_ASSIGN; |
| new_st.label1 = label; |
| new_st.expr1 = expr; |
| return MATCH_YES; |
| } |
| } |
| return MATCH_NO; |
| } |
| |
| |
| /* Match the GO TO statement. As a computed GOTO statement is |
| matched, it is transformed into an equivalent SELECT block. No |
| tree is necessary, and the resulting jumps-to-jumps are |
| specifically optimized away by the back end. */ |
| |
| match |
| gfc_match_goto (void) |
| { |
| gfc_code *head, *tail; |
| gfc_expr *expr; |
| gfc_case *cp; |
| gfc_st_label *label; |
| int i; |
| match m; |
| |
| if (gfc_match (" %l%t", &label) == MATCH_YES) |
| { |
| if (gfc_reference_st_label (label, ST_LABEL_TARGET) == FAILURE) |
| return MATCH_ERROR; |
| |
| new_st.op = EXEC_GOTO; |
| new_st.label1 = label; |
| return MATCH_YES; |
| } |
| |
| /* The assigned GO TO statement. */ |
| |
| if (gfc_match_variable (&expr, 0) == MATCH_YES) |
| { |
| if (gfc_notify_std (GFC_STD_F95_DEL, "Assigned GOTO " |
| "statement at %C") |
| == FAILURE) |
| return MATCH_ERROR; |
| |
| new_st.op = EXEC_GOTO; |
| new_st.expr1 = expr; |
| |
| if (gfc_match_eos () == MATCH_YES) |
| return MATCH_YES; |
| |
| /* Match label list. */ |
| gfc_match_char (','); |
| if (gfc_match_char ('(') != MATCH_YES) |
| { |
| gfc_syntax_error (ST_GOTO); |
| return MATCH_ERROR; |
| } |
| head = tail = NULL; |
| |
| do |
| { |
| m = gfc_match_st_label (&label); |
| if (m != MATCH_YES) |
| goto syntax; |
| |
| if (gfc_reference_st_label (label, ST_LABEL_TARGET) == FAILURE) |
| goto cleanup; |
| |
| if (head == NULL) |
| head = tail = gfc_get_code (); |
| else |
| { |
| tail->block = gfc_get_code (); |
| tail = tail->block; |
| } |
| |
| tail->label1 = label; |
| tail->op = EXEC_GOTO; |
| } |
| while (gfc_match_char (',') == MATCH_YES); |
| |
| if (gfc_match (")%t") != MATCH_YES) |
| goto syntax; |
| |
| if (head == NULL) |
| { |
| gfc_error ("Statement label list in GOTO at %C cannot be empty"); |
| goto syntax; |
| } |
| new_st.block = head; |
| |
| return MATCH_YES; |
| } |
| |
| /* Last chance is a computed GO TO statement. */ |
| if (gfc_match_char ('(') != MATCH_YES) |
| { |
| gfc_syntax_error (ST_GOTO); |
| return MATCH_ERROR; |
| } |
| |
| head = tail = NULL; |
| i = 1; |
| |
| do |
| { |
| m = gfc_match_st_label (&label); |
| if (m != MATCH_YES) |
| goto syntax; |
| |
| if (gfc_reference_st_label (label, ST_LABEL_TARGET) == FAILURE) |
| goto cleanup; |
| |
| if (head == NULL) |
| head = tail = gfc_get_code (); |
| else |
| { |
| tail->block = gfc_get_code (); |
| tail = tail->block; |
| } |
| |
| cp = gfc_get_case (); |
| cp->low = cp->high = gfc_get_int_expr (gfc_default_integer_kind, |
| NULL, i++); |
| |
| tail->op = EXEC_SELECT; |
| tail->ext.block.case_list = cp; |
| |
| tail->next = gfc_get_code (); |
| tail->next->op = EXEC_GOTO; |
| tail->next->label1 = label; |
| } |
| while (gfc_match_char (',') == MATCH_YES); |
| |
| if (gfc_match_char (')') != MATCH_YES) |
| goto syntax; |
| |
| if (head == NULL) |
| { |
| gfc_error ("Statement label list in GOTO at %C cannot be empty"); |
| goto syntax; |
| } |
| |
| /* Get the rest of the statement. */ |
| gfc_match_char (','); |
| |
| if (gfc_match (" %e%t", &expr) != MATCH_YES) |
| goto syntax; |
| |
| if (gfc_notify_std (GFC_STD_F95_OBS, "Computed GOTO " |
| "at %C") == FAILURE) |
| return MATCH_ERROR; |
| |
| /* At this point, a computed GOTO has been fully matched and an |
| equivalent SELECT statement constructed. */ |
| |
| new_st.op = EXEC_SELECT; |
| new_st.expr1 = NULL; |
| |
| /* Hack: For a "real" SELECT, the expression is in expr. We put |
| it in expr2 so we can distinguish then and produce the correct |
| diagnostics. */ |
| new_st.expr2 = expr; |
| new_st.block = head; |
| return MATCH_YES; |
| |
| syntax: |
| gfc_syntax_error (ST_GOTO); |
| cleanup: |
| gfc_free_statements (head); |
| return MATCH_ERROR; |
| } |
| |
| |
| /* Frees a list of gfc_alloc structures. */ |
| |
| void |
| gfc_free_alloc_list (gfc_alloc *p) |
| { |
| gfc_alloc *q; |
| |
| for (; p; p = q) |
| { |
| q = p->next; |
| gfc_free_expr (p->expr); |
| free (p); |
| } |
| } |
| |
| |
| /* Match an ALLOCATE statement. */ |
| |
| match |
| gfc_match_allocate (void) |
| { |
| gfc_alloc *head, *tail; |
| gfc_expr *stat, *errmsg, *tmp, *source, *mold; |
| gfc_typespec ts; |
| gfc_symbol *sym; |
| match m; |
| locus old_locus, deferred_locus; |
| bool saw_stat, saw_errmsg, saw_source, saw_mold, saw_deferred, b1, b2, b3; |
| bool saw_unlimited = false; |
| |
| head = tail = NULL; |
| stat = errmsg = source = mold = tmp = NULL; |
| saw_stat = saw_errmsg = saw_source = saw_mold = saw_deferred = false; |
| |
| if (gfc_match_char ('(') != MATCH_YES) |
| goto syntax; |
| |
| /* Match an optional type-spec. */ |
| old_locus =
|