| Index: gcc/gcc/fortran/decl.c | 
| diff --git a/gcc/gcc/fortran/decl.c b/gcc/gcc/fortran/decl.c | 
| index 36a6d6302da0f6e74161b7208059972240448b0b..692078a11d4f3f3f703c59d75e71fe3d50c10d2e 100644 | 
| --- a/gcc/gcc/fortran/decl.c | 
| +++ b/gcc/gcc/fortran/decl.c | 
| @@ -1,5 +1,5 @@ | 
| /* Declaration statement matcher | 
| -   Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008 | 
| +   Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010 | 
| Free Software Foundation, Inc. | 
| Contributed by Andy Vaught | 
|  | 
| @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see | 
| #include "gfortran.h" | 
| #include "match.h" | 
| #include "parse.h" | 
| +#include "flags.h" | 
|  | 
|  | 
| /* Macros to access allocate memory for gfc_data_variable, | 
| @@ -621,8 +622,8 @@ char_len_param_value (gfc_expr **expr) | 
| if (e->symtree->n.sym->ts.type == BT_UNKNOWN) | 
| goto syntax; | 
| if (e->symtree->n.sym->ts.type == BT_CHARACTER | 
| -		  && e->symtree->n.sym->ts.cl | 
| -		  && e->symtree->n.sym->ts.cl->length->ts.type == BT_UNKNOWN) | 
| +		  && e->symtree->n.sym->ts.u.cl | 
| +		  && e->symtree->n.sym->ts.u.cl->length->ts.type == BT_UNKNOWN) | 
| goto syntax; | 
| } | 
| } | 
| @@ -654,6 +655,9 @@ match_char_length (gfc_expr **expr) | 
|  | 
| if (m == MATCH_YES) | 
| { | 
| +      if (gfc_notify_std (GFC_STD_F95_OBS, "Obsolescent feature: " | 
| +			  "Old-style character length at %C") == FAILURE) | 
| +	return MATCH_ERROR; | 
| *expr = gfc_int_expr (length); | 
| return m; | 
| } | 
| @@ -695,14 +699,18 @@ syntax: | 
| (located in another namespace).  */ | 
|  | 
| static int | 
| -find_special (const char *name, gfc_symbol **result) | 
| +find_special (const char *name, gfc_symbol **result, bool allow_subroutine) | 
| { | 
| gfc_state_data *s; | 
| +  gfc_symtree *st; | 
| int i; | 
|  | 
| -  i = gfc_get_symbol (name, NULL, result); | 
| +  i = gfc_get_sym_tree (name, NULL, &st, allow_subroutine); | 
| if (i == 0) | 
| -    goto end; | 
| +    { | 
| +      *result = st ? st->n.sym : NULL; | 
| +      goto end; | 
| +    } | 
|  | 
| if (gfc_current_state () != COMP_SUBROUTINE | 
| && gfc_current_state () != COMP_FUNCTION) | 
| @@ -930,7 +938,7 @@ verify_c_interop_param (gfc_symbol *sym) | 
| "because derived type '%s' is not C interoperable", | 
| sym->name, &(sym->declared_at), | 
| sym->ns->proc_name->name, | 
| -			   sym->ts.derived->name); | 
| +			   sym->ts.u.derived->name); | 
| else | 
| gfc_warning ("Variable '%s' at %L is a parameter to the " | 
| "BIND(C) procedure '%s' but may not be C " | 
| @@ -943,7 +951,7 @@ verify_c_interop_param (gfc_symbol *sym) | 
| length of 1.  */ | 
| if (sym->ts.type == BT_CHARACTER) | 
| { | 
| -	      gfc_charlen *cl = sym->ts.cl; | 
| +	      gfc_charlen *cl = sym->ts.u.cl; | 
| if (!cl || !cl->length || cl->length->expr_type != EXPR_CONSTANT | 
| || mpz_cmp_si (cl->length->value.integer, 1) != 0) | 
| { | 
| @@ -1017,6 +1025,7 @@ verify_c_interop_param (gfc_symbol *sym) | 
| } | 
|  | 
|  | 
| + | 
| /* Function called by variable_decl() that adds a name to the symbol table.  */ | 
|  | 
| static gfc_try | 
| @@ -1037,7 +1046,7 @@ build_sym (const char *name, gfc_charlen *cl, | 
| return FAILURE; | 
|  | 
| if (sym->ts.type == BT_CHARACTER) | 
| -    sym->ts.cl = cl; | 
| +    sym->ts.u.cl = cl; | 
|  | 
| /* Add dimension attribute if present.  */ | 
| if (gfc_set_array_spec (sym, *as, var_locus) == FAILURE) | 
| @@ -1089,6 +1098,14 @@ build_sym (const char *name, gfc_charlen *cl, | 
|  | 
| sym->attr.implied_index = 0; | 
|  | 
| +  if (sym->ts.type == BT_CLASS) | 
| +    { | 
| +      sym->attr.class_ok = (sym->attr.dummy | 
| +			      || sym->attr.pointer | 
| +			      || sym->attr.allocatable) ? 1 : 0; | 
| +      gfc_build_class_symbol (&sym->ts, &sym->attr, &sym->as); | 
| +    } | 
| + | 
| return SUCCESS; | 
| } | 
|  | 
| @@ -1203,7 +1220,7 @@ add_init_expr_to_sym (const char *name, gfc_expr **initp, locus *var_locus) | 
| gfc_expr *init; | 
|  | 
| init = *initp; | 
| -  if (find_special (name, &sym)) | 
| +  if (find_special (name, &sym, false)) | 
| return FAILURE; | 
|  | 
| attr = sym->attr; | 
| @@ -1242,43 +1259,46 @@ add_init_expr_to_sym (const char *name, gfc_expr **initp, locus *var_locus) | 
| /* Check if the assignment can happen. This has to be put off | 
| until later for a derived type variable.  */ | 
| if (sym->ts.type != BT_DERIVED && init->ts.type != BT_DERIVED | 
| +	  && sym->ts.type != BT_CLASS && init->ts.type != BT_CLASS | 
| && gfc_check_assign_symbol (sym, init) == FAILURE) | 
| return FAILURE; | 
|  | 
| -      if (sym->ts.type == BT_CHARACTER && sym->ts.cl) | 
| +      if (sym->ts.type == BT_CHARACTER && sym->ts.u.cl | 
| +	    && init->ts.type == BT_CHARACTER) | 
| { | 
| /* Update symbol character length according initializer.  */ | 
| -	  if (sym->ts.cl->length == NULL) | 
| +	  if (gfc_check_assign_symbol (sym, init) == FAILURE) | 
| +	    return FAILURE; | 
| + | 
| +	  if (sym->ts.u.cl->length == NULL) | 
| { | 
| int clen; | 
| /* If there are multiple CHARACTER variables declared on the | 
| same line, we don't want them to share the same length.  */ | 
| -	      sym->ts.cl = gfc_get_charlen (); | 
| -	      sym->ts.cl->next = gfc_current_ns->cl_list; | 
| -	      gfc_current_ns->cl_list = sym->ts.cl; | 
| +	      sym->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL); | 
|  | 
| if (sym->attr.flavor == FL_PARAMETER) | 
| { | 
| if (init->expr_type == EXPR_CONSTANT) | 
| { | 
| clen = init->value.character.length; | 
| -		      sym->ts.cl->length = gfc_int_expr (clen); | 
| +		      sym->ts.u.cl->length = gfc_int_expr (clen); | 
| } | 
| else if (init->expr_type == EXPR_ARRAY) | 
| { | 
| gfc_expr *p = init->value.constructor->expr; | 
| clen = p->value.character.length; | 
| -		      sym->ts.cl->length = gfc_int_expr (clen); | 
| +		      sym->ts.u.cl->length = gfc_int_expr (clen); | 
| } | 
| -		  else if (init->ts.cl && init->ts.cl->length) | 
| -		    sym->ts.cl->length = | 
| -				gfc_copy_expr (sym->value->ts.cl->length); | 
| +		  else if (init->ts.u.cl && init->ts.u.cl->length) | 
| +		    sym->ts.u.cl->length = | 
| +				gfc_copy_expr (sym->value->ts.u.cl->length); | 
| } | 
| } | 
| /* Update initializer character length according symbol.  */ | 
| -	  else if (sym->ts.cl->length->expr_type == EXPR_CONSTANT) | 
| +	  else if (sym->ts.u.cl->length->expr_type == EXPR_CONSTANT) | 
| { | 
| -	      int len = mpz_get_si (sym->ts.cl->length->value.integer); | 
| +	      int len = mpz_get_si (sym->ts.u.cl->length->value.integer); | 
| gfc_constructor * p; | 
|  | 
| if (init->expr_type == EXPR_CONSTANT) | 
| @@ -1287,10 +1307,8 @@ add_init_expr_to_sym (const char *name, gfc_expr **initp, locus *var_locus) | 
| { | 
| /* Build a new charlen to prevent simplification from | 
| deleting the length before it is resolved.  */ | 
| -		  init->ts.cl = gfc_get_charlen (); | 
| -		  init->ts.cl->next = gfc_current_ns->cl_list; | 
| -		  gfc_current_ns->cl_list = sym->ts.cl; | 
| -		  init->ts.cl->length = gfc_copy_expr (sym->ts.cl->length); | 
| +		  init->ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL); | 
| +		  init->ts.u.cl->length = gfc_copy_expr (sym->ts.u.cl->length); | 
|  | 
| for (p = init->value.constructor; p; p = p->next) | 
| gfc_set_constant_character_len (len, p->expr, -1); | 
| @@ -1377,11 +1395,12 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
| gfc_array_spec **as) | 
| { | 
| gfc_component *c; | 
| +  gfc_try t = SUCCESS; | 
|  | 
| -  /* If the current symbol is of the same derived type that we're | 
| +  /* F03:C438/C439. If the current symbol is of the same derived type that we're | 
| constructing, it must have the pointer attribute.  */ | 
| -  if (current_ts.type == BT_DERIVED | 
| -      && current_ts.derived == gfc_current_block () | 
| +  if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS) | 
| +      && current_ts.u.derived == gfc_current_block () | 
| && current_attr.pointer == 0) | 
| { | 
| gfc_error ("Component at %C must have the POINTER attribute"); | 
| @@ -1402,7 +1421,8 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
| return FAILURE; | 
|  | 
| c->ts = current_ts; | 
| -  c->ts.cl = cl; | 
| +  if (c->ts.type == BT_CHARACTER) | 
| +    c->ts.u.cl = cl; | 
| c->attr = current_attr; | 
|  | 
| c->initializer = *init; | 
| @@ -1415,62 +1435,52 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
|  | 
| /* Should this ever get more complicated, combine with similar section | 
| in add_init_expr_to_sym into a separate function.  */ | 
| -  if (c->ts.type == BT_CHARACTER && !c->attr.pointer && c->initializer && c->ts.cl | 
| -      && c->ts.cl->length && c->ts.cl->length->expr_type == EXPR_CONSTANT) | 
| +  if (c->ts.type == BT_CHARACTER && !c->attr.pointer && c->initializer && c->ts.u.cl | 
| +      && c->ts.u.cl->length && c->ts.u.cl->length->expr_type == EXPR_CONSTANT) | 
| { | 
| int len; | 
|  | 
| -      gcc_assert (c->ts.cl && c->ts.cl->length); | 
| -      gcc_assert (c->ts.cl->length->expr_type == EXPR_CONSTANT); | 
| -      gcc_assert (c->ts.cl->length->ts.type == BT_INTEGER); | 
| +      gcc_assert (c->ts.u.cl && c->ts.u.cl->length); | 
| +      gcc_assert (c->ts.u.cl->length->expr_type == EXPR_CONSTANT); | 
| +      gcc_assert (c->ts.u.cl->length->ts.type == BT_INTEGER); | 
|  | 
| -      len = mpz_get_si (c->ts.cl->length->value.integer); | 
| +      len = mpz_get_si (c->ts.u.cl->length->value.integer); | 
|  | 
| if (c->initializer->expr_type == EXPR_CONSTANT) | 
| gfc_set_constant_character_len (len, c->initializer, -1); | 
| -      else if (mpz_cmp (c->ts.cl->length->value.integer, | 
| -			c->initializer->ts.cl->length->value.integer)) | 
| +      else if (mpz_cmp (c->ts.u.cl->length->value.integer, | 
| +			c->initializer->ts.u.cl->length->value.integer)) | 
| { | 
| bool has_ts; | 
| gfc_constructor *ctor = c->initializer->value.constructor; | 
|  | 
| -	  bool first = true; | 
| -	  int first_len; | 
| - | 
| -	  has_ts = (c->initializer->ts.cl | 
| -		    && c->initializer->ts.cl->length_from_typespec); | 
| +	  has_ts = (c->initializer->ts.u.cl | 
| +		    && c->initializer->ts.u.cl->length_from_typespec); | 
|  | 
| -	  for (; ctor; ctor = ctor->next) | 
| +	  if (ctor) | 
| { | 
| -	      /* Remember the length of the first element for checking that | 
| -		 all elements *in the constructor* have the same length.  This | 
| -		 need not be the length of the LHS!  */ | 
| -	      if (first) | 
| +	      int first_len; | 
| + | 
| +	      /* Remember the length of the first element for checking | 
| +		 that all elements *in the constructor* have the same | 
| +		 length.  This need not be the length of the LHS!  */ | 
| +	      gcc_assert (ctor->expr->expr_type == EXPR_CONSTANT); | 
| +	      gcc_assert (ctor->expr->ts.type == BT_CHARACTER); | 
| +	      first_len = ctor->expr->value.character.length; | 
| + | 
| +	      for (; ctor; ctor = ctor->next) | 
| { | 
| -		  gcc_assert (ctor->expr->expr_type == EXPR_CONSTANT); | 
| -		  gcc_assert (ctor->expr->ts.type == BT_CHARACTER); | 
| -		  first_len = ctor->expr->value.character.length; | 
| -		  first = false; | 
| +		  if (ctor->expr->expr_type == EXPR_CONSTANT) | 
| +		    gfc_set_constant_character_len (len, ctor->expr, | 
| +						    has_ts ? -1 : first_len); | 
| } | 
| - | 
| -	      if (ctor->expr->expr_type == EXPR_CONSTANT) | 
| -		gfc_set_constant_character_len (len, ctor->expr, | 
| -						has_ts ? -1 : first_len); | 
| } | 
| } | 
| } | 
|  | 
| /* Check array components.  */ | 
| if (!c->attr.dimension) | 
| -    { | 
| -      if (c->attr.allocatable) | 
| -	{ | 
| -	  gfc_error ("Allocatable component at %C must be an array"); | 
| -	  return FAILURE; | 
| -	} | 
| -      else | 
| -	return SUCCESS; | 
| -    } | 
| +    goto scalar; | 
|  | 
| if (c->attr.pointer) | 
| { | 
| @@ -1478,7 +1488,7 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
| { | 
| gfc_error ("Pointer array component of structure at %C must have a " | 
| "deferred shape"); | 
| -	  return FAILURE; | 
| +	  t = FAILURE; | 
| } | 
| } | 
| else if (c->attr.allocatable) | 
| @@ -1487,7 +1497,7 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
| { | 
| gfc_error ("Allocatable component of structure at %C must have a " | 
| "deferred shape"); | 
| -	  return FAILURE; | 
| +	  t = FAILURE; | 
| } | 
| } | 
| else | 
| @@ -1496,11 +1506,15 @@ build_struct (const char *name, gfc_charlen *cl, gfc_expr **init, | 
| { | 
| gfc_error ("Array component of structure at %C must have an " | 
| "explicit shape"); | 
| -	  return FAILURE; | 
| +	  t = FAILURE; | 
| } | 
| } | 
|  | 
| -  return SUCCESS; | 
| +scalar: | 
| +  if (c->ts.type == BT_CLASS) | 
| +    gfc_build_class_symbol (&c->ts, &c->attr, &c->as); | 
| + | 
| +  return t; | 
| } | 
|  | 
|  | 
| @@ -1560,12 +1574,10 @@ variable_decl (int elem) | 
| match m; | 
| gfc_try t; | 
| gfc_symbol *sym; | 
| -  locus old_locus; | 
|  | 
| initializer = NULL; | 
| as = NULL; | 
| cp_as = NULL; | 
| -  old_locus = gfc_current_locus; | 
|  | 
| /* When we get here, we've just matched a list of attributes and | 
| maybe a type and a double colon.  The next thing we expect to see | 
| @@ -1594,9 +1606,7 @@ variable_decl (int elem) | 
| switch (match_char_length (&char_len)) | 
| { | 
| case MATCH_YES: | 
| -	  cl = gfc_get_charlen (); | 
| -	  cl->next = gfc_current_ns->cl_list; | 
| -	  gfc_current_ns->cl_list = cl; | 
| +	  cl = gfc_new_charlen (gfc_current_ns, NULL); | 
|  | 
| cl->length = char_len; | 
| break; | 
| @@ -1605,16 +1615,14 @@ variable_decl (int elem) | 
| element.  Also copy assumed lengths.  */ | 
| case MATCH_NO: | 
| if (elem > 1 | 
| -	      && (current_ts.cl->length == NULL | 
| -		  || current_ts.cl->length->expr_type != EXPR_CONSTANT)) | 
| +	      && (current_ts.u.cl->length == NULL | 
| +		  || current_ts.u.cl->length->expr_type != EXPR_CONSTANT)) | 
| { | 
| -	      cl = gfc_get_charlen (); | 
| -	      cl->next = gfc_current_ns->cl_list; | 
| -	      gfc_current_ns->cl_list = cl; | 
| -	      cl->length = gfc_copy_expr (current_ts.cl->length); | 
| +	      cl = gfc_new_charlen (gfc_current_ns, NULL); | 
| +	      cl->length = gfc_copy_expr (current_ts.u.cl->length); | 
| } | 
| else | 
| -	    cl = current_ts.cl; | 
| +	    cl = current_ts.u.cl; | 
|  | 
| break; | 
|  | 
| @@ -1632,8 +1640,8 @@ variable_decl (int elem) | 
| { | 
| sym->ts.type = current_ts.type; | 
| sym->ts.kind = current_ts.kind; | 
| -	  sym->ts.cl = cl; | 
| -	  sym->ts.derived = current_ts.derived; | 
| +	  sym->ts.u.cl = cl; | 
| +	  sym->ts.u.derived = current_ts.u.derived; | 
| sym->ts.is_c_interop = current_ts.is_c_interop; | 
| sym->ts.is_iso_c = current_ts.is_iso_c; | 
| m = MATCH_YES; | 
| @@ -1667,6 +1675,17 @@ variable_decl (int elem) | 
| } | 
| } | 
|  | 
| +  /* Procedure pointer as function result.  */ | 
| +  if (gfc_current_state () == COMP_FUNCTION | 
| +      && strcmp ("ppr@", gfc_current_block ()->name) == 0 | 
| +      && strcmp (name, gfc_current_block ()->ns->proc_name->name) == 0) | 
| +    strcpy (name, "ppr@"); | 
| + | 
| +  if (gfc_current_state () == COMP_FUNCTION | 
| +      && strcmp (name, gfc_current_block ()->name) == 0 | 
| +      && gfc_current_block ()->result | 
| +      && strcmp ("ppr@", gfc_current_block ()->result->name) == 0) | 
| +    strcpy (name, "ppr@"); | 
|  | 
| /* OK, we've successfully matched the declaration.  Now put the | 
| symbol in the current namespace, because it might be used in the | 
| @@ -1694,13 +1713,13 @@ variable_decl (int elem) | 
| if (current_ts.type == BT_DERIVED | 
| && gfc_current_ns->proc_name | 
| && gfc_current_ns->proc_name->attr.if_source == IFSRC_IFBODY | 
| -      && current_ts.derived->ns != gfc_current_ns) | 
| +      && current_ts.u.derived->ns != gfc_current_ns) | 
| { | 
| gfc_symtree *st; | 
| -      st = gfc_find_symtree (gfc_current_ns->sym_root, current_ts.derived->name); | 
| -      if (!(current_ts.derived->attr.imported | 
| +      st = gfc_find_symtree (gfc_current_ns->sym_root, current_ts.u.derived->name); | 
| +      if (!(current_ts.u.derived->attr.imported | 
| && st != NULL | 
| -		&& st->n.sym == current_ts.derived) | 
| +		&& st->n.sym == current_ts.u.derived) | 
| && !gfc_current_ns->has_import_set) | 
| { | 
| gfc_error ("the type of '%s' at %C has not been declared within the " | 
| @@ -1762,7 +1781,7 @@ variable_decl (int elem) | 
| m = MATCH_ERROR; | 
| } | 
|  | 
| -	  if (gfc_pure (NULL)) | 
| +	  if (gfc_pure (NULL) && gfc_state_stack->state != COMP_DERIVED) | 
| { | 
| gfc_error ("Initialization of pointer at %C is not allowed in " | 
| "a PURE procedure"); | 
| @@ -1790,7 +1809,8 @@ variable_decl (int elem) | 
| m = MATCH_ERROR; | 
| } | 
|  | 
| -	  if (current_attr.flavor != FL_PARAMETER && gfc_pure (NULL)) | 
| +	  if (current_attr.flavor != FL_PARAMETER && gfc_pure (NULL) | 
| +	      && gfc_state_stack->state != COMP_DERIVED) | 
| { | 
| gfc_error ("Initialization of variable at %C is not allowed in " | 
| "a PURE procedure"); | 
| @@ -1989,9 +2009,9 @@ kind_expr: | 
| if (ts->f90_type != BT_UNKNOWN && ts->f90_type != ts->type | 
| && !((ts->f90_type == BT_REAL && ts->type == BT_COMPLEX) | 
| || (ts->f90_type == BT_COMPLEX && ts->type == BT_REAL))) | 
| -    gfc_error_now ("C kind type parameter is for type %s but type at %L " | 
| -		   "is %s", gfc_basic_typename (ts->f90_type), &where, | 
| -		   gfc_basic_typename (ts->type)); | 
| +    gfc_warning_now ("C kind type parameter is for type %s but type at %L " | 
| +		     "is %s", gfc_basic_typename (ts->f90_type), &where, | 
| +		     gfc_basic_typename (ts->type)); | 
|  | 
| gfc_gobble_whitespace (); | 
| if ((c = gfc_next_ascii_char ()) != ')' | 
| @@ -2090,11 +2110,12 @@ no_match: | 
| return m; | 
| } | 
|  | 
| + | 
| /* Match the various kind/length specifications in a CHARACTER | 
| declaration.  We don't return MATCH_NO.  */ | 
|  | 
| -static match | 
| -match_char_spec (gfc_typespec *ts) | 
| +match | 
| +gfc_match_char_spec (gfc_typespec *ts) | 
| { | 
| int kind, seen_length, is_iso_c; | 
| gfc_charlen *cl; | 
| @@ -2221,16 +2242,14 @@ done: | 
| } | 
|  | 
| /* Do some final massaging of the length values.  */ | 
| -  cl = gfc_get_charlen (); | 
| -  cl->next = gfc_current_ns->cl_list; | 
| -  gfc_current_ns->cl_list = cl; | 
| +  cl = gfc_new_charlen (gfc_current_ns, NULL); | 
|  | 
| if (seen_length == 0) | 
| cl->length = gfc_int_expr (1); | 
| else | 
| cl->length = len; | 
|  | 
| -  ts->cl = cl; | 
| +  ts->u.cl = cl; | 
| ts->kind = kind == 0 ? gfc_default_character_kind : kind; | 
|  | 
| /* We have to know if it was a c interoperable kind so we can | 
| @@ -2254,8 +2273,8 @@ done: | 
| } | 
|  | 
|  | 
| -/* Matches a type specification.  If successful, sets the ts structure | 
| -   to the matched specification.  This is necessary for FUNCTION and | 
| +/* Matches a declaration-type-spec (F03:R502).  If successful, sets the ts | 
| +   structure to the matched specification.  This is necessary for FUNCTION and | 
| IMPLICIT statements. | 
|  | 
| If implicit_flag is nonzero, then we don't check for the optional | 
| @@ -2263,7 +2282,7 @@ done: | 
| statement correctly.  */ | 
|  | 
| match | 
| -gfc_match_type_spec (gfc_typespec *ts, int implicit_flag) | 
| +gfc_match_decl_type_spec (gfc_typespec *ts, int implicit_flag) | 
| { | 
| char name[GFC_MAX_SYMBOL_LEN + 1]; | 
| gfc_symbol *sym; | 
| @@ -2285,7 +2304,7 @@ gfc_match_type_spec (gfc_typespec *ts, int implicit_flag) | 
|  | 
| if (gfc_match (" byte") == MATCH_YES) | 
| { | 
| -      if (gfc_notify_std(GFC_STD_GNU, "Extension: BYTE type at %C") | 
| +      if (gfc_notify_std (GFC_STD_GNU, "Extension: BYTE type at %C") | 
| == FAILURE) | 
| return MATCH_ERROR; | 
|  | 
| @@ -2312,7 +2331,7 @@ gfc_match_type_spec (gfc_typespec *ts, int implicit_flag) | 
| { | 
| ts->type = BT_CHARACTER; | 
| if (implicit_flag == 0) | 
| -	return match_char_spec (ts); | 
| +	return gfc_match_char_spec (ts); | 
| else | 
| return MATCH_YES; | 
| } | 
| @@ -2357,20 +2376,29 @@ gfc_match_type_spec (gfc_typespec *ts, int implicit_flag) | 
| } | 
|  | 
| m = gfc_match (" type ( %n )", name); | 
| -  if (m != MATCH_YES) | 
| -    return m; | 
| +  if (m == MATCH_YES) | 
| +    ts->type = BT_DERIVED; | 
| +  else | 
| +    { | 
| +      m = gfc_match (" class ( %n )", name); | 
| +      if (m != MATCH_YES) | 
| +	return m; | 
| +      ts->type = BT_CLASS; | 
|  | 
| -  ts->type = BT_DERIVED; | 
| +      if (gfc_notify_std (GFC_STD_F2003, "Fortran 2003: CLASS statement at %C") | 
| +			  == FAILURE) | 
| +	return MATCH_ERROR; | 
| +    } | 
|  | 
| /* Defer association of the derived type until the end of the | 
| specification block.  However, if the derived type can be | 
| found, add it to the typespec.  */ | 
| if (gfc_matching_function) | 
| { | 
| -      ts->derived = NULL; | 
| +      ts->u.derived = NULL; | 
| if (gfc_current_state () != COMP_INTERFACE | 
| && !gfc_find_symbol (name, NULL, 1, &sym) && sym) | 
| -	ts->derived = sym; | 
| +	ts->u.derived = sym; | 
| return MATCH_YES; | 
| } | 
|  | 
| @@ -2403,7 +2431,7 @@ gfc_match_type_spec (gfc_typespec *ts, int implicit_flag) | 
| return MATCH_ERROR; | 
|  | 
| gfc_set_sym_referenced (sym); | 
| -  ts->derived = sym; | 
| +  ts->u.derived = sym; | 
|  | 
| return MATCH_YES; | 
|  | 
| @@ -2415,8 +2443,8 @@ get_kind: | 
|  | 
| if (gfc_current_form == FORM_FREE) | 
| { | 
| -      c = gfc_peek_ascii_char(); | 
| -      if (!gfc_is_whitespace(c) && c != '*' && c != '(' | 
| +      c = gfc_peek_ascii_char (); | 
| +      if (!gfc_is_whitespace (c) && c != '*' && c != '(' | 
| && c != ':' && c != ',') | 
| return MATCH_NO; | 
| } | 
| @@ -2577,7 +2605,7 @@ gfc_match_implicit (void) | 
| gfc_clear_new_implicit (); | 
|  | 
| /* A basic type is mandatory here.  */ | 
| -      m = gfc_match_type_spec (&ts, 1); | 
| +      m = gfc_match_decl_type_spec (&ts, 1); | 
| if (m == MATCH_ERROR) | 
| goto error; | 
| if (m == MATCH_NO) | 
| @@ -2594,13 +2622,11 @@ gfc_match_implicit (void) | 
| if ((c == '\n') || (c == ',')) | 
| { | 
| /* Check for CHARACTER with no length parameter.  */ | 
| -	      if (ts.type == BT_CHARACTER && !ts.cl) | 
| +	      if (ts.type == BT_CHARACTER && !ts.u.cl) | 
| { | 
| ts.kind = gfc_default_character_kind; | 
| -		  ts.cl = gfc_get_charlen (); | 
| -		  ts.cl->next = gfc_current_ns->cl_list; | 
| -		  gfc_current_ns->cl_list = ts.cl; | 
| -		  ts.cl->length = gfc_int_expr (1); | 
| +		  ts.u.cl = gfc_new_charlen (gfc_current_ns, NULL); | 
| +		  ts.u.cl->length = gfc_int_expr (1); | 
| } | 
|  | 
| /* Record the Successful match.  */ | 
| @@ -2617,7 +2643,7 @@ gfc_match_implicit (void) | 
|  | 
| /* Last chance -- check <TYPE> <SELECTOR> (<RANGE>).  */ | 
| if (ts.type == BT_CHARACTER) | 
| -	m = match_char_spec (&ts); | 
| +	m = gfc_match_char_spec (&ts); | 
| else | 
| { | 
| m = gfc_match_kind_spec (&ts, false); | 
| @@ -2730,7 +2756,7 @@ gfc_match_import (void) | 
| goto next_item; | 
| } | 
|  | 
| -	  st = gfc_new_symtree (&gfc_current_ns->sym_root, name); | 
| +	  st = gfc_new_symtree (&gfc_current_ns->sym_root, sym->name); | 
| st->n.sym = sym; | 
| sym->refs++; | 
| sym->attr.imported = 1; | 
| @@ -2794,7 +2820,7 @@ match_attr_spec (void) | 
| DECL_IN, DECL_OUT, DECL_INOUT, DECL_INTRINSIC, DECL_OPTIONAL, | 
| DECL_PARAMETER, DECL_POINTER, DECL_PROTECTED, DECL_PRIVATE, | 
| DECL_PUBLIC, DECL_SAVE, DECL_TARGET, DECL_VALUE, DECL_VOLATILE, | 
| -    DECL_IS_BIND_C, DECL_NONE, | 
| +    DECL_IS_BIND_C, DECL_ASYNCHRONOUS, DECL_NONE, | 
| GFC_DECL_END /* Sentinel */ | 
| } | 
| decl_types; | 
| @@ -2804,7 +2830,7 @@ match_attr_spec (void) | 
|  | 
| locus start, seen_at[NUM_DECL]; | 
| int seen[NUM_DECL]; | 
| -  decl_types d; | 
| +  unsigned int d; | 
| const char *attr; | 
| match m; | 
| gfc_try t; | 
| @@ -2839,9 +2865,25 @@ match_attr_spec (void) | 
| switch (gfc_peek_ascii_char ()) | 
| { | 
| case 'a': | 
| -	      if (match_string_p ("allocatable")) | 
| -		d = DECL_ALLOCATABLE; | 
| -	      break; | 
| +	      gfc_next_ascii_char (); | 
| +	      switch (gfc_next_ascii_char ()) | 
| +		{ | 
| +		case 'l': | 
| +		  if (match_string_p ("locatable")) | 
| +		    { | 
| +		      /* Matched "allocatable".  */ | 
| +		      d = DECL_ALLOCATABLE; | 
| +		    } | 
| +		  break; | 
| + | 
| +		case 's': | 
| +		  if (match_string_p ("ynchronous")) | 
| +		    { | 
| +		      /* Matched "asynchronous".  */ | 
| +		      d = DECL_ASYNCHRONOUS; | 
| +		    } | 
| +		  break; | 
| +		} | 
|  | 
| case 'b': | 
| /* Try and match the bind(c).  */ | 
| @@ -3022,6 +3064,9 @@ match_attr_spec (void) | 
| case DECL_ALLOCATABLE: | 
| attr = "ALLOCATABLE"; | 
| break; | 
| +	  case DECL_ASYNCHRONOUS: | 
| +	    attr = "ASYNCHRONOUS"; | 
| +	    break; | 
| case DECL_DIMENSION: | 
| attr = "DIMENSION"; | 
| break; | 
| @@ -3148,6 +3193,15 @@ match_attr_spec (void) | 
| t = gfc_add_allocatable (¤t_attr, &seen_at[d]); | 
| break; | 
|  | 
| +	case DECL_ASYNCHRONOUS: | 
| +	  if (gfc_notify_std (GFC_STD_F2003, | 
| +			      "Fortran 2003: ASYNCHRONOUS attribute at %C") | 
| +	      == FAILURE) | 
| +	    t = FAILURE; | 
| +	  else | 
| +	    t = gfc_add_asynchronous (¤t_attr, NULL, &seen_at[d]); | 
| +	  break; | 
| + | 
| case DECL_DIMENSION: | 
| t = gfc_add_dimension (¤t_attr, NULL, &seen_at[d]); | 
| break; | 
| @@ -3312,8 +3366,8 @@ set_com_block_bind_c (gfc_common_head *com_block, int is_bind_c) | 
| gfc_try | 
| verify_c_interop (gfc_typespec *ts) | 
| { | 
| -  if (ts->type == BT_DERIVED && ts->derived != NULL) | 
| -    return (ts->derived->ts.is_c_interop ? SUCCESS : FAILURE); | 
| +  if (ts->type == BT_DERIVED && ts->u.derived != NULL) | 
| +    return (ts->u.derived->ts.is_c_interop ? SUCCESS : FAILURE); | 
| else if (ts->is_c_interop != 1) | 
| return FAILURE; | 
|  | 
| @@ -3455,9 +3509,9 @@ verify_bind_c_sym (gfc_symbol *tmp_sym, gfc_typespec *ts, | 
|  | 
| /* BIND(C) functions can not return a character string.  */ | 
| if (bind_c_function && tmp_sym->ts.type == BT_CHARACTER) | 
| -	if (tmp_sym->ts.cl == NULL || tmp_sym->ts.cl->length == NULL | 
| -	    || tmp_sym->ts.cl->length->expr_type != EXPR_CONSTANT | 
| -	    || mpz_cmp_si (tmp_sym->ts.cl->length->value.integer, 1) != 0) | 
| +	if (tmp_sym->ts.u.cl == NULL || tmp_sym->ts.u.cl->length == NULL | 
| +	    || tmp_sym->ts.u.cl->length->expr_type != EXPR_CONSTANT | 
| +	    || mpz_cmp_si (tmp_sym->ts.u.cl->length->value.integer, 1) != 0) | 
| gfc_error ("Return type of BIND(C) function '%s' at %L cannot " | 
| "be a character string", tmp_sym->name, | 
| &(tmp_sym->declared_at)); | 
| @@ -3655,13 +3709,14 @@ gfc_match_data_decl (void) | 
|  | 
| num_idents_on_line = 0; | 
|  | 
| -  m = gfc_match_type_spec (¤t_ts, 0); | 
| +  m = gfc_match_decl_type_spec (¤t_ts, 0); | 
| if (m != MATCH_YES) | 
| return m; | 
|  | 
| -  if (current_ts.type == BT_DERIVED && gfc_current_state () != COMP_DERIVED) | 
| +  if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS) | 
| +	&& gfc_current_state () != COMP_DERIVED) | 
| { | 
| -      sym = gfc_use_derived (current_ts.derived); | 
| +      sym = gfc_use_derived (current_ts.u.derived); | 
|  | 
| if (sym == NULL) | 
| { | 
| @@ -3669,7 +3724,7 @@ gfc_match_data_decl (void) | 
| goto cleanup; | 
| } | 
|  | 
| -      current_ts.derived = sym; | 
| +      current_ts.u.derived = sym; | 
| } | 
|  | 
| m = match_attr_spec (); | 
| @@ -3679,21 +3734,22 @@ gfc_match_data_decl (void) | 
| goto cleanup; | 
| } | 
|  | 
| -  if (current_ts.type == BT_DERIVED && current_ts.derived->components == NULL | 
| -      && !current_ts.derived->attr.zero_comp) | 
| +  if ((current_ts.type == BT_DERIVED || current_ts.type == BT_CLASS) | 
| +      && current_ts.u.derived->components == NULL | 
| +      && !current_ts.u.derived->attr.zero_comp) | 
| { | 
|  | 
| if (current_attr.pointer && gfc_current_state () == COMP_DERIVED) | 
| goto ok; | 
|  | 
| -      gfc_find_symbol (current_ts.derived->name, | 
| -		       current_ts.derived->ns->parent, 1, &sym); | 
| +      gfc_find_symbol (current_ts.u.derived->name, | 
| +		       current_ts.u.derived->ns->parent, 1, &sym); | 
|  | 
| /* Any symbol that we find had better be a type definition | 
| which has its components defined.  */ | 
| if (sym != NULL && sym->attr.flavor == FL_DERIVED | 
| -	  && (current_ts.derived->components != NULL | 
| -	      || current_ts.derived->attr.zero_comp)) | 
| +	  && (current_ts.u.derived->components != NULL | 
| +	      || current_ts.u.derived->attr.zero_comp)) | 
| goto ok; | 
|  | 
| /* Now we have an error, which we signal, and then fix up | 
| @@ -3760,7 +3816,7 @@ gfc_match_prefix (gfc_typespec *ts) | 
|  | 
| loop: | 
| if (!seen_type && ts != NULL | 
| -      && gfc_match_type_spec (ts, 0) == MATCH_YES | 
| +      && gfc_match_decl_type_spec (ts, 0) == MATCH_YES | 
| && gfc_match_space () == MATCH_YES) | 
| { | 
|  | 
| @@ -4069,19 +4125,85 @@ gfc_match_suffix (gfc_symbol *sym, gfc_symbol **result) | 
| } | 
|  | 
|  | 
| -/* Match a PROCEDURE declaration (R1211).  */ | 
| +/* Procedure pointer return value without RESULT statement: | 
| +   Add "hidden" result variable named "ppr@".  */ | 
| + | 
| +static gfc_try | 
| +add_hidden_procptr_result (gfc_symbol *sym) | 
| +{ | 
| +  bool case1,case2; | 
| + | 
| +  if (gfc_notification_std (GFC_STD_F2003) == ERROR) | 
| +    return FAILURE; | 
| + | 
| +  /* First usage case: PROCEDURE and EXTERNAL statements.  */ | 
| +  case1 = gfc_current_state () == COMP_FUNCTION && gfc_current_block () | 
| +	  && strcmp (gfc_current_block ()->name, sym->name) == 0 | 
| +	  && sym->attr.external; | 
| +  /* Second usage case: INTERFACE statements.  */ | 
| +  case2 = gfc_current_state () == COMP_INTERFACE && gfc_state_stack->previous | 
| +	  && gfc_state_stack->previous->state == COMP_FUNCTION | 
| +	  && strcmp (gfc_state_stack->previous->sym->name, sym->name) == 0; | 
| + | 
| +  if (case1 || case2) | 
| +    { | 
| +      gfc_symtree *stree; | 
| +      if (case1) | 
| +	gfc_get_sym_tree ("ppr@", gfc_current_ns, &stree, false); | 
| +      else if (case2) | 
| +	{ | 
| +	  gfc_symtree *st2; | 
| +	  gfc_get_sym_tree ("ppr@", gfc_current_ns->parent, &stree, false); | 
| +	  st2 = gfc_new_symtree (&gfc_current_ns->sym_root, "ppr@"); | 
| +	  st2->n.sym = stree->n.sym; | 
| +	} | 
| +      sym->result = stree->n.sym; | 
| + | 
| +      sym->result->attr.proc_pointer = sym->attr.proc_pointer; | 
| +      sym->result->attr.pointer = sym->attr.pointer; | 
| +      sym->result->attr.external = sym->attr.external; | 
| +      sym->result->attr.referenced = sym->attr.referenced; | 
| +      sym->result->ts = sym->ts; | 
| +      sym->attr.proc_pointer = 0; | 
| +      sym->attr.pointer = 0; | 
| +      sym->attr.external = 0; | 
| +      if (sym->result->attr.external && sym->result->attr.pointer) | 
| +	{ | 
| +	  sym->result->attr.pointer = 0; | 
| +	  sym->result->attr.proc_pointer = 1; | 
| +	} | 
| + | 
| +      return gfc_add_result (&sym->result->attr, sym->result->name, NULL); | 
| +    } | 
| +  /* POINTER after PROCEDURE/EXTERNAL/INTERFACE statement.  */ | 
| +  else if (sym->attr.function && !sym->attr.external && sym->attr.pointer | 
| +	   && sym->result && sym->result != sym && sym->result->attr.external | 
| +	   && sym == gfc_current_ns->proc_name | 
| +	   && sym == sym->result->ns->proc_name | 
| +	   && strcmp ("ppr@", sym->result->name) == 0) | 
| +    { | 
| +      sym->result->attr.proc_pointer = 1; | 
| +      sym->attr.pointer = 0; | 
| +      return SUCCESS; | 
| +    } | 
| +  else | 
| +    return FAILURE; | 
| +} | 
| + | 
| + | 
| +/* Match the interface for a PROCEDURE declaration, | 
| +   including brackets (R1212).  */ | 
|  | 
| static match | 
| -match_procedure_decl (void) | 
| +match_procedure_interface (gfc_symbol **proc_if) | 
| { | 
| match m; | 
| +  gfc_symtree *st; | 
| locus old_loc, entry_loc; | 
| -  gfc_symbol *sym, *proc_if = NULL; | 
| -  int num; | 
| -  gfc_expr *initializer = NULL; | 
| +  gfc_namespace *old_ns = gfc_current_ns; | 
| +  char name[GFC_MAX_SYMBOL_LEN + 1]; | 
|  | 
| old_loc = entry_loc = gfc_current_locus; | 
| - | 
| gfc_clear_ts (¤t_ts); | 
|  | 
| if (gfc_match (" (") != MATCH_YES) | 
| @@ -4092,7 +4214,7 @@ match_procedure_decl (void) | 
|  | 
| /* Get the type spec. for the procedure interface.  */ | 
| old_loc = gfc_current_locus; | 
| -  m = gfc_match_type_spec (¤t_ts, 0); | 
| +  m = gfc_match_decl_type_spec (¤t_ts, 0); | 
| gfc_gobble_whitespace (); | 
| if (m == MATCH_YES || (m == MATCH_NO && gfc_peek_ascii_char () == ')')) | 
| goto got_ts; | 
| @@ -4100,49 +4222,59 @@ match_procedure_decl (void) | 
| if (m == MATCH_ERROR) | 
| return m; | 
|  | 
| +  /* Procedure interface is itself a procedure.  */ | 
| gfc_current_locus = old_loc; | 
| +  m = gfc_match_name (name); | 
|  | 
| -  /* Get the name of the procedure or abstract interface | 
| -  to inherit the interface from.  */ | 
| -  m = gfc_match_symbol (&proc_if, 1); | 
| +  /* First look to see if it is already accessible in the current | 
| +     namespace because it is use associated or contained.  */ | 
| +  st = NULL; | 
| +  if (gfc_find_sym_tree (name, NULL, 0, &st)) | 
| +    return MATCH_ERROR; | 
|  | 
| -  if (m == MATCH_NO) | 
| -    goto syntax; | 
| -  else if (m == MATCH_ERROR) | 
| -    return m; | 
| +  /* If it is still not found, then try the parent namespace, if it | 
| +     exists and create the symbol there if it is still not found.  */ | 
| +  if (gfc_current_ns->parent) | 
| +    gfc_current_ns = gfc_current_ns->parent; | 
| +  if (st == NULL && gfc_get_ha_sym_tree (name, &st)) | 
| +    return MATCH_ERROR; | 
| + | 
| +  gfc_current_ns = old_ns; | 
| +  *proc_if = st->n.sym; | 
|  | 
| /* Various interface checks.  */ | 
| -  if (proc_if) | 
| +  if (*proc_if) | 
| { | 
| -      proc_if->refs++; | 
| +      (*proc_if)->refs++; | 
| /* Resolve interface if possible. That way, attr.procedure is only set | 
| if it is declared by a later procedure-declaration-stmt, which is | 
| invalid per C1212.  */ | 
| -      while (proc_if->ts.interface) | 
| -	proc_if = proc_if->ts.interface; | 
| +      while ((*proc_if)->ts.interface) | 
| +	*proc_if = (*proc_if)->ts.interface; | 
|  | 
| -      if (proc_if->generic) | 
| +      if ((*proc_if)->generic) | 
| { | 
| -	  gfc_error ("Interface '%s' at %C may not be generic", proc_if->name); | 
| +	  gfc_error ("Interface '%s' at %C may not be generic", | 
| +		     (*proc_if)->name); | 
| return MATCH_ERROR; | 
| } | 
| -      if (proc_if->attr.proc == PROC_ST_FUNCTION) | 
| +      if ((*proc_if)->attr.proc == PROC_ST_FUNCTION) | 
| { | 
| gfc_error ("Interface '%s' at %C may not be a statement function", | 
| -		    proc_if->name); | 
| +		     (*proc_if)->name); | 
| return MATCH_ERROR; | 
| } | 
| /* Handle intrinsic procedures.  */ | 
| -      if (!(proc_if->attr.external || proc_if->attr.use_assoc | 
| -	    || proc_if->attr.if_source == IFSRC_IFBODY) | 
| -	  && (gfc_is_intrinsic (proc_if, 0, gfc_current_locus) | 
| -	      || gfc_is_intrinsic (proc_if, 1, gfc_current_locus))) | 
| -	proc_if->attr.intrinsic = 1; | 
| -      if (proc_if->attr.intrinsic | 
| -	  && !gfc_intrinsic_actual_ok (proc_if->name, 0)) | 
| +      if (!((*proc_if)->attr.external || (*proc_if)->attr.use_assoc | 
| +	    || (*proc_if)->attr.if_source == IFSRC_IFBODY) | 
| +	  && (gfc_is_intrinsic ((*proc_if), 0, gfc_current_locus) | 
| +	      || gfc_is_intrinsic ((*proc_if), 1, gfc_current_locus))) | 
| +	(*proc_if)->attr.intrinsic = 1; | 
| +      if ((*proc_if)->attr.intrinsic | 
| +	  && !gfc_intrinsic_actual_ok ((*proc_if)->name, 0)) | 
| { | 
| gfc_error ("Intrinsic procedure '%s' not allowed " | 
| -		    "in PROCEDURE statement at %C", proc_if->name); | 
| +		    "in PROCEDURE statement at %C", (*proc_if)->name); | 
| return MATCH_ERROR; | 
| } | 
| } | 
| @@ -4154,7 +4286,26 @@ got_ts: | 
| return MATCH_NO; | 
| } | 
|  | 
| -  /* Parse attributes.  */ | 
| +  return MATCH_YES; | 
| +} | 
| + | 
| + | 
| +/* Match a PROCEDURE declaration (R1211).  */ | 
| + | 
| +static match | 
| +match_procedure_decl (void) | 
| +{ | 
| +  match m; | 
| +  gfc_symbol *sym, *proc_if = NULL; | 
| +  int num; | 
| +  gfc_expr *initializer = NULL; | 
| + | 
| +  /* Parse interface (with brackets). */ | 
| +  m = match_procedure_interface (&proc_if); | 
| +  if (m != MATCH_YES) | 
| +    return m; | 
| + | 
| +  /* Parse attributes (with colons).  */ | 
| m = match_attr_spec(); | 
| if (m == MATCH_ERROR) | 
| return MATCH_ERROR; | 
| @@ -4201,22 +4352,36 @@ got_ts: | 
|  | 
| if (gfc_add_external (&sym->attr, NULL) == FAILURE) | 
| return MATCH_ERROR; | 
| + | 
| +      if (add_hidden_procptr_result (sym) == SUCCESS) | 
| +	sym = sym->result; | 
| + | 
| if (gfc_add_proc (&sym->attr, sym->name, NULL) == FAILURE) | 
| return MATCH_ERROR; | 
|  | 
| /* Set interface.  */ | 
| if (proc_if != NULL) | 
| { | 
| +          if (sym->ts.type != BT_UNKNOWN) | 
| +	    { | 
| +	      gfc_error ("Procedure '%s' at %L already has basic type of %s", | 
| +			 sym->name, &gfc_current_locus, | 
| +			 gfc_basic_typename (sym->ts.type)); | 
| +	      return MATCH_ERROR; | 
| +	    } | 
| sym->ts.interface = proc_if; | 
| sym->attr.untyped = 1; | 
| +	  sym->attr.if_source = IFSRC_IFBODY; | 
| } | 
| else if (current_ts.type != BT_UNKNOWN) | 
| { | 
| -	  sym->ts = current_ts; | 
| +	  if (gfc_add_type (sym, ¤t_ts, &gfc_current_locus) == FAILURE) | 
| +	    return MATCH_ERROR; | 
| sym->ts.interface = gfc_new_symbol ("", gfc_current_ns); | 
| sym->ts.interface->ts = current_ts; | 
| sym->ts.interface->attr.function = 1; | 
| sym->attr.function = sym->ts.interface->attr.function; | 
| +	  sym->attr.if_source = IFSRC_UNKNOWN; | 
| } | 
|  | 
| if (gfc_match (" =>") == MATCH_YES) | 
| @@ -4270,6 +4435,136 @@ cleanup: | 
| } | 
|  | 
|  | 
| +static match | 
| +match_binding_attributes (gfc_typebound_proc* ba, bool generic, bool ppc); | 
| + | 
| + | 
| +/* Match a procedure pointer component declaration (R445).  */ | 
| + | 
| +static match | 
| +match_ppc_decl (void) | 
| +{ | 
| +  match m; | 
| +  gfc_symbol *proc_if = NULL; | 
| +  gfc_typespec ts; | 
| +  int num; | 
| +  gfc_component *c; | 
| +  gfc_expr *initializer = NULL; | 
| +  gfc_typebound_proc* tb; | 
| +  char name[GFC_MAX_SYMBOL_LEN + 1]; | 
| + | 
| +  /* Parse interface (with brackets).  */ | 
| +  m = match_procedure_interface (&proc_if); | 
| +  if (m != MATCH_YES) | 
| +    goto syntax; | 
| + | 
| +  /* Parse attributes.  */ | 
| +  tb = XCNEW (gfc_typebound_proc); | 
| +  tb->where = gfc_current_locus; | 
| +  m = match_binding_attributes (tb, false, true); | 
| +  if (m == MATCH_ERROR) | 
| +    return m; | 
| + | 
| +  gfc_clear_attr (¤t_attr); | 
| +  current_attr.procedure = 1; | 
| +  current_attr.proc_pointer = 1; | 
| +  current_attr.access = tb->access; | 
| +  current_attr.flavor = FL_PROCEDURE; | 
| + | 
| +  /* Match the colons (required).  */ | 
| +  if (gfc_match (" ::") != MATCH_YES) | 
| +    { | 
| +      gfc_error ("Expected '::' after binding-attributes at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| +  /* Check for C450.  */ | 
| +  if (!tb->nopass && proc_if == NULL) | 
| +    { | 
| +      gfc_error("NOPASS or explicit interface required at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| +  if (gfc_notify_std (GFC_STD_F2003, "Fortran 2003: Procedure pointer " | 
| +                     "component at %C") == FAILURE) | 
| +    return MATCH_ERROR; | 
| + | 
| +  /* Match PPC names.  */ | 
| +  ts = current_ts; | 
| +  for(num=1;;num++) | 
| +    { | 
| +      m = gfc_match_name (name); | 
| +      if (m == MATCH_NO) | 
| +	goto syntax; | 
| +      else if (m == MATCH_ERROR) | 
| +	return m; | 
| + | 
| +      if (gfc_add_component (gfc_current_block (), name, &c) == FAILURE) | 
| +	return MATCH_ERROR; | 
| + | 
| +      /* Add current_attr to the symbol attributes.  */ | 
| +      if (gfc_copy_attr (&c->attr, ¤t_attr, NULL) == FAILURE) | 
| +	return MATCH_ERROR; | 
| + | 
| +      if (gfc_add_external (&c->attr, NULL) == FAILURE) | 
| +	return MATCH_ERROR; | 
| + | 
| +      if (gfc_add_proc (&c->attr, name, NULL) == FAILURE) | 
| +	return MATCH_ERROR; | 
| + | 
| +      c->tb = tb; | 
| + | 
| +      /* Set interface.  */ | 
| +      if (proc_if != NULL) | 
| +	{ | 
| +	  c->ts.interface = proc_if; | 
| +	  c->attr.untyped = 1; | 
| +	  c->attr.if_source = IFSRC_IFBODY; | 
| +	} | 
| +      else if (ts.type != BT_UNKNOWN) | 
| +	{ | 
| +	  c->ts = ts; | 
| +	  c->ts.interface = gfc_new_symbol ("", gfc_current_ns); | 
| +	  c->ts.interface->ts = ts; | 
| +	  c->ts.interface->attr.function = 1; | 
| +	  c->attr.function = c->ts.interface->attr.function; | 
| +	  c->attr.if_source = IFSRC_UNKNOWN; | 
| +	} | 
| + | 
| +      if (gfc_match (" =>") == MATCH_YES) | 
| +	{ | 
| +	  m = gfc_match_null (&initializer); | 
| +	  if (m == MATCH_NO) | 
| +	    { | 
| +	      gfc_error ("Pointer initialization requires a NULL() at %C"); | 
| +	      m = MATCH_ERROR; | 
| +	    } | 
| +	  if (gfc_pure (NULL)) | 
| +	    { | 
| +	      gfc_error ("Initialization of pointer at %C is not allowed in " | 
| +			 "a PURE procedure"); | 
| +	      m = MATCH_ERROR; | 
| +	    } | 
| +	  if (m != MATCH_YES) | 
| +	    { | 
| +	      gfc_free_expr (initializer); | 
| +	      return m; | 
| +	    } | 
| +	  c->initializer = initializer; | 
| +	} | 
| + | 
| +      if (gfc_match_eos () == MATCH_YES) | 
| +	return MATCH_YES; | 
| +      if (gfc_match_char (',') != MATCH_YES) | 
| +	goto syntax; | 
| +    } | 
| + | 
| +syntax: | 
| +  gfc_error ("Syntax error in procedure pointer component at %C"); | 
| +  return MATCH_ERROR; | 
| +} | 
| + | 
| + | 
| /* Match a PROCEDURE declaration inside an interface (R1206).  */ | 
|  | 
| static match | 
| @@ -4335,9 +4630,8 @@ gfc_match_procedure (void) | 
| m = match_procedure_in_interface (); | 
| break; | 
| case COMP_DERIVED: | 
| -      gfc_error ("Fortran 2003: Procedure components at %C are not yet" | 
| -		 " implemented in gfortran"); | 
| -      return MATCH_ERROR; | 
| +      m = match_ppc_decl (); | 
| +      break; | 
| case COMP_DERIVED_CONTAINS: | 
| m = match_procedure_in_type (); | 
| break; | 
| @@ -4407,6 +4701,10 @@ gfc_match_function_decl (void) | 
| } | 
| if (get_proc_name (name, &sym, false)) | 
| return MATCH_ERROR; | 
| + | 
| +  if (add_hidden_procptr_result (sym) == SUCCESS) | 
| +    sym = sym->result; | 
| + | 
| gfc_new_block = sym; | 
|  | 
| m = gfc_match_formal_arglist (sym, 0, 0); | 
| @@ -4469,14 +4767,6 @@ gfc_match_function_decl (void) | 
| || copy_prefix (&sym->attr, &sym->declared_at) == FAILURE) | 
| goto cleanup; | 
|  | 
| -      if (current_ts.type != BT_UNKNOWN && sym->ts.type != BT_UNKNOWN | 
| -	  && !sym->attr.implicit_type) | 
| -	{ | 
| -	  gfc_error ("Function '%s' at %C already has a type of %s", name, | 
| -		     gfc_basic_typename (sym->ts.type)); | 
| -	  goto cleanup; | 
| -	} | 
| - | 
| /* Delay matching the function characteristics until after the | 
| specification block by signalling kind=-1.  */ | 
| sym->declared_at = old_loc; | 
| @@ -4487,12 +4777,17 @@ gfc_match_function_decl (void) | 
|  | 
| if (result == NULL) | 
| { | 
| -	  sym->ts = current_ts; | 
| +          if (current_ts.type != BT_UNKNOWN | 
| +	      && gfc_add_type (sym, ¤t_ts, &gfc_current_locus) == FAILURE) | 
| +	    goto cleanup; | 
| sym->result = sym; | 
| } | 
| else | 
| { | 
| -	  result->ts = current_ts; | 
| +          if (current_ts.type != BT_UNKNOWN | 
| +	      && gfc_add_type (result, ¤t_ts, &gfc_current_locus) | 
| +		 == FAILURE) | 
| +	    goto cleanup; | 
| sym->result = result; | 
| } | 
|  | 
| @@ -4516,7 +4811,7 @@ static bool | 
| add_global_entry (const char *name, int sub) | 
| { | 
| gfc_gsymbol *s; | 
| -  unsigned int type; | 
| +  enum gfc_symbol_type type; | 
|  | 
| s = gfc_get_gsymbol(name); | 
| type = sub ? GSYM_SUBROUTINE : GSYM_FUNCTION; | 
| @@ -4530,6 +4825,7 @@ add_global_entry (const char *name, int sub) | 
| s->type = type; | 
| s->where = gfc_current_locus; | 
| s->defined = 1; | 
| +      s->ns = gfc_current_ns; | 
| return true; | 
| } | 
| return false; | 
| @@ -4803,6 +5099,14 @@ gfc_match_subroutine (void) | 
|  | 
| if (get_proc_name (name, &sym, false)) | 
| return MATCH_ERROR; | 
| + | 
| +  /* Set declared_at as it might point to, e.g., a PUBLIC statement, if | 
| +     the symbol existed before. */ | 
| +  sym->declared_at = gfc_current_locus; | 
| + | 
| +  if (add_hidden_procptr_result (sym) == SUCCESS) | 
| +    sym = sym->result; | 
| + | 
| gfc_new_block = sym; | 
|  | 
| /* Check what next non-whitespace character is so we can tell if there | 
| @@ -5054,7 +5358,7 @@ set_enum_kind(void) | 
| if (max_enum == NULL || enum_history == NULL) | 
| return; | 
|  | 
| -  if (!gfc_option.fshort_enums) | 
| +  if (!flag_short_enums) | 
| return; | 
|  | 
| i = 0; | 
| @@ -5076,8 +5380,8 @@ set_enum_kind(void) | 
|  | 
|  | 
| /* Match any of the various end-block statements.  Returns the type of | 
| -   END to the caller.  The END INTERFACE, END IF, END DO and END | 
| -   SELECT statements cannot be replaced by a single END statement.  */ | 
| +   END to the caller.  The END INTERFACE, END IF, END DO, END SELECT | 
| +   and END BLOCK statements cannot be replaced by a single END statement.  */ | 
|  | 
| match | 
| gfc_match_end (gfc_statement *st) | 
| @@ -5098,6 +5402,9 @@ gfc_match_end (gfc_statement *st) | 
| block_name = gfc_current_block () == NULL | 
| ? NULL : gfc_current_block ()->name; | 
|  | 
| +  if (state == COMP_BLOCK && !strcmp (block_name, "block@")) | 
| +    block_name = NULL; | 
| + | 
| if (state == COMP_CONTAINS || state == COMP_DERIVED_CONTAINS) | 
| { | 
| state = gfc_state_stack->previous->state; | 
| @@ -5151,6 +5458,12 @@ gfc_match_end (gfc_statement *st) | 
| eos_ok = 0; | 
| break; | 
|  | 
| +    case COMP_BLOCK: | 
| +      *st = ST_END_BLOCK; | 
| +      target = " block"; | 
| +      eos_ok = 0; | 
| +      break; | 
| + | 
| case COMP_IF: | 
| *st = ST_ENDIF; | 
| target = " if"; | 
| @@ -5164,6 +5477,7 @@ gfc_match_end (gfc_statement *st) | 
| break; | 
|  | 
| case COMP_SELECT: | 
| +    case COMP_SELECT_TYPE: | 
| *st = ST_END_SELECT; | 
| target = " select"; | 
| eos_ok = 0; | 
| @@ -5220,10 +5534,10 @@ gfc_match_end (gfc_statement *st) | 
| { | 
|  | 
| if (*st != ST_ENDDO && *st != ST_ENDIF && *st != ST_END_SELECT | 
| -	  && *st != ST_END_FORALL && *st != ST_END_WHERE) | 
| +	  && *st != ST_END_FORALL && *st != ST_END_WHERE && *st != ST_END_BLOCK) | 
| return MATCH_YES; | 
|  | 
| -      if (gfc_current_block () == NULL) | 
| +      if (!block_name) | 
| return MATCH_YES; | 
|  | 
| gfc_error ("Expected block name of '%s' in %s statement at %C", | 
| @@ -5250,12 +5564,21 @@ gfc_match_end (gfc_statement *st) | 
| if (block_name == NULL) | 
| goto syntax; | 
|  | 
| -  if (strcmp (name, block_name) != 0) | 
| +  if (strcmp (name, block_name) != 0 && strcmp (block_name, "ppr@") != 0) | 
| { | 
| gfc_error ("Expected label '%s' for %s statement at %C", block_name, | 
| gfc_ascii_statement (*st)); | 
| goto cleanup; | 
| } | 
| +  /* Procedure pointer as function result.  */ | 
| +  else if (strcmp (block_name, "ppr@") == 0 | 
| +	   && strcmp (name, gfc_current_block ()->ns->proc_name->name) != 0) | 
| +    { | 
| +      gfc_error ("Expected label '%s' for %s statement at %C", | 
| +		 gfc_current_block ()->ns->proc_name->name, | 
| +		 gfc_ascii_statement (*st)); | 
| +      goto cleanup; | 
| +    } | 
|  | 
| if (gfc_match_eos () == MATCH_YES) | 
| return MATCH_YES; | 
| @@ -5289,7 +5612,7 @@ attr_decl1 (void) | 
| if (m != MATCH_YES) | 
| goto cleanup; | 
|  | 
| -  if (find_special (name, &sym)) | 
| +  if (find_special (name, &sym, false)) | 
| return MATCH_ERROR; | 
|  | 
| var_locus = gfc_current_locus; | 
| @@ -5329,13 +5652,31 @@ attr_decl1 (void) | 
| } | 
| } | 
|  | 
| -  /* Update symbol table.  DIMENSION attribute is set | 
| -     in gfc_set_array_spec().  */ | 
| -  if (current_attr.dimension == 0 | 
| -      && gfc_copy_attr (&sym->attr, ¤t_attr, &var_locus) == FAILURE) | 
| +  /* Update symbol table.  DIMENSION attribute is set in | 
| +     gfc_set_array_spec().  For CLASS variables, this must be applied | 
| +     to the first component, or '$data' field.  */ | 
| +  if (sym->ts.type == BT_CLASS && sym->ts.u.derived) | 
| { | 
| -      m = MATCH_ERROR; | 
| -      goto cleanup; | 
| +      gfc_component *comp; | 
| +      comp = gfc_find_component (sym->ts.u.derived, "$data", true, true); | 
| +      if (comp == NULL || gfc_copy_attr (&comp->attr, ¤t_attr, | 
| +					 &var_locus) == FAILURE) | 
| +	{ | 
| +	  m = MATCH_ERROR; | 
| +	  goto cleanup; | 
| +	} | 
| +      sym->attr.class_ok = (sym->attr.class_ok | 
| +			      || current_attr.allocatable | 
| +			      || current_attr.pointer); | 
| +    } | 
| +  else | 
| +    { | 
| +      if (current_attr.dimension == 0 | 
| +	    && gfc_copy_attr (&sym->attr, ¤t_attr, &var_locus) == FAILURE) | 
| +	{ | 
| +	  m = MATCH_ERROR; | 
| +	  goto cleanup; | 
| +	} | 
| } | 
|  | 
| if (gfc_set_array_spec (sym, as, &var_locus) == FAILURE) | 
| @@ -5366,6 +5707,8 @@ attr_decl1 (void) | 
| goto cleanup; | 
| } | 
|  | 
| +  add_hidden_procptr_result (sym); | 
| + | 
| return MATCH_YES; | 
|  | 
| cleanup: | 
| @@ -5575,6 +5918,13 @@ gfc_match_intent (void) | 
| { | 
| sym_intent intent; | 
|  | 
| +  /* This is not allowed within a BLOCK construct!  */ | 
| +  if (gfc_current_state () == COMP_BLOCK) | 
| +    { | 
| +      gfc_error ("INTENT is not allowed inside of BLOCK at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| intent = match_intent_spec (); | 
| if (intent == INTENT_UNKNOWN) | 
| return MATCH_ERROR; | 
| @@ -5600,6 +5950,12 @@ gfc_match_intrinsic (void) | 
| match | 
| gfc_match_optional (void) | 
| { | 
| +  /* This is not allowed within a BLOCK construct!  */ | 
| +  if (gfc_current_state () == COMP_BLOCK) | 
| +    { | 
| +      gfc_error ("OPTIONAL is not allowed inside of BLOCK at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
|  | 
| gfc_clear_attr (¤t_attr); | 
| current_attr.optional = 1; | 
| @@ -6057,6 +6413,13 @@ gfc_match_value (void) | 
| gfc_symbol *sym; | 
| match m; | 
|  | 
| +  /* This is not allowed within a BLOCK construct!  */ | 
| +  if (gfc_current_state () == COMP_BLOCK) | 
| +    { | 
| +      gfc_error ("VALUE is not allowed inside of BLOCK at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| if (gfc_notify_std (GFC_STD_F2003, "Fortran 2003: VALUE statement at %C") | 
| == FAILURE) | 
| return MATCH_ERROR; | 
| @@ -6155,6 +6518,59 @@ syntax: | 
| } | 
|  | 
|  | 
| +match | 
| +gfc_match_asynchronous (void) | 
| +{ | 
| +  gfc_symbol *sym; | 
| +  match m; | 
| + | 
| +  if (gfc_notify_std (GFC_STD_F2003, "Fortran 2003: ASYNCHRONOUS statement at %C") | 
| +      == FAILURE) | 
| +    return MATCH_ERROR; | 
| + | 
| +  if (gfc_match (" ::") == MATCH_NO && gfc_match_space () == MATCH_NO) | 
| +    { | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| +  if (gfc_match_eos () == MATCH_YES) | 
| +    goto syntax; | 
| + | 
| +  for(;;) | 
| +    { | 
| +      /* ASYNCHRONOUS is special because it can be added to host-associated | 
| +	 symbols locally.  */ | 
| +      m = gfc_match_symbol (&sym, 1); | 
| +      switch (m) | 
| +	{ | 
| +	case MATCH_YES: | 
| +	  if (gfc_add_asynchronous (&sym->attr, sym->name, &gfc_current_locus) | 
| +	      == FAILURE) | 
| +	    return MATCH_ERROR; | 
| +	  goto next_item; | 
| + | 
| +	case MATCH_NO: | 
| +	  break; | 
| + | 
| +	case MATCH_ERROR: | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| +    next_item: | 
| +      if (gfc_match_eos () == MATCH_YES) | 
| +	break; | 
| +      if (gfc_match_char (',') != MATCH_YES) | 
| +	goto syntax; | 
| +    } | 
| + | 
| +  return MATCH_YES; | 
| + | 
| +syntax: | 
| +  gfc_error ("Syntax error in ASYNCHRONOUS statement at %C"); | 
| +  return MATCH_ERROR; | 
| +} | 
| + | 
| + | 
| /* Match a module procedure statement.  Note that we have to modify | 
| symbols in the parent's namespace because the current one was there | 
| to receive symbols that are in an interface's formal argument list.  */ | 
| @@ -6180,7 +6596,10 @@ gfc_match_modproc (void) | 
|  | 
| module_ns = gfc_current_ns->parent; | 
| for (; module_ns; module_ns = module_ns->parent) | 
| -    if (module_ns->proc_name->attr.flavor == FL_MODULE) | 
| +    if (module_ns->proc_name->attr.flavor == FL_MODULE | 
| +	|| module_ns->proc_name->attr.flavor == FL_PROGRAM | 
| +	|| (module_ns->proc_name->attr.flavor == FL_PROCEDURE | 
| +	    && !module_ns->proc_name->attr.contained)) | 
| break; | 
|  | 
| if (module_ns == NULL) | 
| @@ -6192,6 +6611,7 @@ gfc_match_modproc (void) | 
|  | 
| for (;;) | 
| { | 
| +      locus old_locus = gfc_current_locus; | 
| bool last = false; | 
|  | 
| m = gfc_match_name (name); | 
| @@ -6212,6 +6632,13 @@ gfc_match_modproc (void) | 
| if (gfc_get_symbol (name, module_ns, &sym)) | 
| return MATCH_ERROR; | 
|  | 
| +      if (sym->attr.intrinsic) | 
| +	{ | 
| +	  gfc_error ("Intrinsic procedure at %L cannot be a MODULE " | 
| +		     "PROCEDURE", &old_locus); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| if (sym->attr.proc != PROC_MODULE | 
| && gfc_add_procedure (&sym->attr, PROC_MODULE, | 
| sym->name, NULL) == FAILURE) | 
| @@ -6221,6 +6648,7 @@ gfc_match_modproc (void) | 
| return MATCH_ERROR; | 
|  | 
| sym->attr.mod_proc = 1; | 
| +      sym->declared_at = old_locus; | 
|  | 
| if (last) | 
| break; | 
| @@ -6357,6 +6785,46 @@ gfc_get_type_attr_spec (symbol_attribute *attr, char *name) | 
| } | 
|  | 
|  | 
| +/* Assign a hash value for a derived type. The algorithm is that of | 
| +   SDBM. The hashed string is '[module_name #] derived_name'.  */ | 
| +static unsigned int | 
| +hash_value (gfc_symbol *sym) | 
| +{ | 
| +  unsigned int hash = 0; | 
| +  const char *c; | 
| +  int i, len; | 
| + | 
| +  /* Hash of the module or procedure name.  */ | 
| +  if (sym->module != NULL) | 
| +    c = sym->module; | 
| +  else if (sym->ns && sym->ns->proc_name | 
| +	     && sym->ns->proc_name->attr.flavor == FL_MODULE) | 
| +    c = sym->ns->proc_name->name; | 
| +  else | 
| +    c = NULL; | 
| + | 
| +  if (c) | 
| +    { | 
| +      len = strlen (c); | 
| +      for (i = 0; i < len; i++, c++) | 
| +	hash =  (hash << 6) + (hash << 16) - hash + (*c); | 
| + | 
| +      /* Disambiguate between 'a' in 'aa' and 'aa' in 'a'.  */ | 
| +      hash =  (hash << 6) + (hash << 16) - hash + '#'; | 
| +    } | 
| + | 
| +  /* Hash of the derived type name.  */ | 
| +  len = strlen (sym->name); | 
| +  c = sym->name; | 
| +  for (i = 0; i < len; i++, c++) | 
| +    hash = (hash << 6) + (hash << 16) - hash + (*c); | 
| + | 
| +  /* Return the hash but take the modulus for the sake of module read, | 
| +     even though this slightly increases the chance of collision.  */ | 
| +  return (hash % 100000000); | 
| +} | 
| + | 
| + | 
| /* Match the beginning of a derived type declaration.  If a type name | 
| was the result of a function, then it is possible to have a symbol | 
| already to be known as a derived type yet have no components.  */ | 
| @@ -6462,13 +6930,23 @@ gfc_match_derived_decl (void) | 
|  | 
| /* Add the extended derived type as the first component.  */ | 
| gfc_add_component (sym, parent, &p); | 
| -      sym->attr.extension = attr.extension; | 
| extended->refs++; | 
| gfc_set_sym_referenced (extended); | 
|  | 
| p->ts.type = BT_DERIVED; | 
| -      p->ts.derived = extended; | 
| +      p->ts.u.derived = extended; | 
| p->initializer = gfc_default_initializer (&p->ts); | 
| + | 
| +      /* Set extension level.  */ | 
| +      if (extended->attr.extension == 255) | 
| +	{ | 
| +	  /* Since the extension field is 8 bit wide, we can only have | 
| +	     up to 255 extension levels.  */ | 
| +	  gfc_error ("Maximum extension level reached with type '%s' at %L", | 
| +		     extended->name, &extended->declared_at); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| +      sym->attr.extension = extended->attr.extension + 1; | 
|  | 
| /* Provide the links between the extended type and its extension.  */ | 
| if (!extended->f2k_derived) | 
| @@ -6477,6 +6955,10 @@ gfc_match_derived_decl (void) | 
| st->n.sym = sym; | 
| } | 
|  | 
| +  if (!sym->hash_value) | 
| +    /* Set the hash for the compound name for this type.  */ | 
| +    sym->hash_value = hash_value (sym); | 
| + | 
| /* Take over the ABSTRACT attribute.  */ | 
| sym->attr.abstract = attr.abstract; | 
|  | 
| @@ -6487,22 +6969,14 @@ gfc_match_derived_decl (void) | 
|  | 
|  | 
| /* Cray Pointees can be declared as: | 
| -      pointer (ipt, a (n,m,...,*)) | 
| -   By default, this is treated as an AS_ASSUMED_SIZE array.  We'll | 
| -   cheat and set a constant bound of 1 for the last dimension, if this | 
| -   is the case. Since there is no bounds-checking for Cray Pointees, | 
| -   this will be okay.  */ | 
| +      pointer (ipt, a (n,m,...,*))  */ | 
|  | 
| -gfc_try | 
| +match | 
| gfc_mod_pointee_as (gfc_array_spec *as) | 
| { | 
| as->cray_pointee = true; /* This will be useful to know later.  */ | 
| if (as->type == AS_ASSUMED_SIZE) | 
| -    { | 
| -      as->type = AS_EXPLICIT; | 
| -      as->upper[as->rank - 1] = gfc_int_expr (1); | 
| -      as->cp_was_assumed = true; | 
| -    } | 
| +    as->cp_was_assumed = true; | 
| else if (as->type == AS_ASSUMED_SHAPE) | 
| { | 
| gfc_error ("Cray Pointee at %C cannot be assumed shape array"); | 
| @@ -6533,6 +7007,51 @@ gfc_match_enum (void) | 
| } | 
|  | 
|  | 
| +/* Returns an initializer whose value is one higher than the value of the | 
| +   LAST_INITIALIZER argument.  If the argument is NULL, the | 
| +   initializers value will be set to zero.  The initializer's kind | 
| +   will be set to gfc_c_int_kind. | 
| + | 
| +   If -fshort-enums is given, the appropriate kind will be selected | 
| +   later after all enumerators have been parsed.  A warning is issued | 
| +   here if an initializer exceeds gfc_c_int_kind.  */ | 
| + | 
| +static gfc_expr * | 
| +enum_initializer (gfc_expr *last_initializer, locus where) | 
| +{ | 
| +  gfc_expr *result; | 
| + | 
| +  result = gfc_get_expr (); | 
| +  result->expr_type = EXPR_CONSTANT; | 
| +  result->ts.type = BT_INTEGER; | 
| +  result->ts.kind = gfc_c_int_kind; | 
| +  result->where = where; | 
| + | 
| +  mpz_init (result->value.integer); | 
| + | 
| +  if (last_initializer != NULL) | 
| +    { | 
| +      mpz_add_ui (result->value.integer, last_initializer->value.integer, 1); | 
| +      result->where = last_initializer->where; | 
| + | 
| +      if (gfc_check_integer_range (result->value.integer, | 
| +	     gfc_c_int_kind) != ARITH_OK) | 
| +	{ | 
| +	  gfc_error ("Enumerator exceeds the C integer type at %C"); | 
| +	  return NULL; | 
| +	} | 
| +    } | 
| +  else | 
| +    { | 
| +      /* Control comes here, if it's the very first enumerator and no | 
| +	 initializer has been given.  It will be initialized to zero.  */ | 
| +      mpz_set_si (result->value.integer, 0); | 
| +    } | 
| + | 
| +  return result; | 
| +} | 
| + | 
| + | 
| /* Match a variable name with an optional initializer.  When this | 
| subroutine is called, a variable is expected to be parsed next. | 
| Depending on what is happening at the moment, updates either the | 
| @@ -6593,14 +7112,13 @@ enumerator_decl (void) | 
| previous enumerator (stored in last_initializer) is incremented | 
| by 1 and is used to initialize the current enumerator.  */ | 
| if (initializer == NULL) | 
| -    initializer = gfc_enum_initializer (last_initializer, old_locus); | 
| +    initializer = enum_initializer (last_initializer, old_locus); | 
|  | 
| if (initializer == NULL || initializer->ts.type != BT_INTEGER) | 
| { | 
| -      gfc_error("ENUMERATOR %L not initialized with integer expression", | 
| -		&var_locus); | 
| +      gfc_error ("ENUMERATOR %L not initialized with integer expression", | 
| +		 &var_locus); | 
| m = MATCH_ERROR; | 
| -      gfc_free_enum_history (); | 
| goto cleanup; | 
| } | 
|  | 
| @@ -6666,7 +7184,10 @@ gfc_match_enumerator_def (void) | 
| { | 
| m = enumerator_decl (); | 
| if (m == MATCH_ERROR) | 
| -	goto cleanup; | 
| +	{ | 
| +	  gfc_free_enum_history (); | 
| +	  goto cleanup; | 
| +	} | 
| if (m == MATCH_NO) | 
| break; | 
|  | 
| @@ -6694,10 +7215,11 @@ cleanup: | 
| /* Match binding attributes.  */ | 
|  | 
| static match | 
| -match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| +match_binding_attributes (gfc_typebound_proc* ba, bool generic, bool ppc) | 
| { | 
| bool found_passing = false; | 
| -  match m; | 
| +  bool seen_ptr = false; | 
| +  match m = MATCH_YES; | 
|  | 
| /* Intialize to defaults.  Do so even before the MATCH_NO check so that in | 
| this case the defaults are in there.  */ | 
| @@ -6706,13 +7228,13 @@ match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| ba->pass_arg_num = 0; | 
| ba->nopass = 0; | 
| ba->non_overridable = 0; | 
| +  ba->deferred = 0; | 
| +  ba->ppc = ppc; | 
|  | 
| /* If we find a comma, we believe there are binding attributes.  */ | 
| -  if (gfc_match_char (',') == MATCH_NO) | 
| -    { | 
| -      ba->access = gfc_typebound_default_access; | 
| -      return MATCH_NO; | 
| -    } | 
| +  m = gfc_match_char (','); | 
| +  if (m == MATCH_NO) | 
| +    goto done; | 
|  | 
| do | 
| { | 
| @@ -6770,33 +7292,6 @@ match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| continue; | 
| } | 
|  | 
| -	  /* NON_OVERRIDABLE flag.  */ | 
| -	  m = gfc_match (" non_overridable"); | 
| -	  if (m == MATCH_ERROR) | 
| -	    goto error; | 
| -	  if (m == MATCH_YES) | 
| -	    { | 
| -	      if (ba->non_overridable) | 
| -		{ | 
| -		  gfc_error ("Duplicate NON_OVERRIDABLE at %C"); | 
| -		  goto error; | 
| -		} | 
| - | 
| -	      ba->non_overridable = 1; | 
| -	      continue; | 
| -	    } | 
| - | 
| -	  /* DEFERRED flag.  */ | 
| -	  /* TODO: Handle really once implemented.  */ | 
| -	  m = gfc_match (" deferred"); | 
| -	  if (m == MATCH_ERROR) | 
| -	    goto error; | 
| -	  if (m == MATCH_YES) | 
| -	    { | 
| -	      gfc_error ("DEFERRED not yet implemented at %C"); | 
| -	      goto error; | 
| -	    } | 
| - | 
| /* PASS possibly including argument.  */ | 
| m = gfc_match (" pass"); | 
| if (m == MATCH_ERROR) | 
| @@ -6816,7 +7311,7 @@ match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| if (m == MATCH_ERROR) | 
| goto error; | 
| if (m == MATCH_YES) | 
| -		ba->pass_arg = xstrdup (arg); | 
| +		ba->pass_arg = gfc_get_string (arg); | 
| gcc_assert ((m == MATCH_YES) == (ba->pass_arg != NULL)); | 
|  | 
| found_passing = true; | 
| @@ -6824,6 +7319,59 @@ match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| continue; | 
| } | 
|  | 
| +	  if (ppc) | 
| +	    { | 
| +	      /* POINTER flag.  */ | 
| +	      m = gfc_match (" pointer"); | 
| +	      if (m == MATCH_ERROR) | 
| +		goto error; | 
| +	      if (m == MATCH_YES) | 
| +		{ | 
| +		  if (seen_ptr) | 
| +		    { | 
| +		      gfc_error ("Duplicate POINTER attribute at %C"); | 
| +		      goto error; | 
| +		    } | 
| + | 
| +		  seen_ptr = true; | 
| +        	  continue; | 
| +		} | 
| +	    } | 
| +	  else | 
| +	    { | 
| +	      /* NON_OVERRIDABLE flag.  */ | 
| +	      m = gfc_match (" non_overridable"); | 
| +	      if (m == MATCH_ERROR) | 
| +		goto error; | 
| +	      if (m == MATCH_YES) | 
| +		{ | 
| +		  if (ba->non_overridable) | 
| +		    { | 
| +		      gfc_error ("Duplicate NON_OVERRIDABLE at %C"); | 
| +		      goto error; | 
| +		    } | 
| + | 
| +		  ba->non_overridable = 1; | 
| +		  continue; | 
| +		} | 
| + | 
| +	      /* DEFERRED flag.  */ | 
| +	      m = gfc_match (" deferred"); | 
| +	      if (m == MATCH_ERROR) | 
| +		goto error; | 
| +	      if (m == MATCH_YES) | 
| +		{ | 
| +		  if (ba->deferred) | 
| +		    { | 
| +		      gfc_error ("Duplicate DEFERRED at %C"); | 
| +		      goto error; | 
| +		    } | 
| + | 
| +		  ba->deferred = 1; | 
| +		  continue; | 
| +		} | 
| +	    } | 
| + | 
| } | 
|  | 
| /* Nothing matching found.  */ | 
| @@ -6835,13 +7383,29 @@ match_binding_attributes (gfc_typebound_proc* ba, bool generic) | 
| } | 
| while (gfc_match_char (',') == MATCH_YES); | 
|  | 
| +  /* NON_OVERRIDABLE and DEFERRED exclude themselves.  */ | 
| +  if (ba->non_overridable && ba->deferred) | 
| +    { | 
| +      gfc_error ("NON_OVERRIDABLE and DEFERRED can't both appear at %C"); | 
| +      goto error; | 
| +    } | 
| + | 
| +  m = MATCH_YES; | 
| + | 
| +done: | 
| if (ba->access == ACCESS_UNKNOWN) | 
| ba->access = gfc_typebound_default_access; | 
|  | 
| -  return MATCH_YES; | 
| +  if (ppc && !seen_ptr) | 
| +    { | 
| +      gfc_error ("POINTER attribute is required for procedure pointer component" | 
| +                 " at %C"); | 
| +      goto error; | 
| +    } | 
| + | 
| +  return m; | 
|  | 
| error: | 
| -  gfc_free (ba->pass_arg); | 
| return MATCH_ERROR; | 
| } | 
|  | 
| @@ -6853,7 +7417,7 @@ match_procedure_in_type (void) | 
| { | 
| char name[GFC_MAX_SYMBOL_LEN + 1]; | 
| char target_buf[GFC_MAX_SYMBOL_LEN + 1]; | 
| -  char* target; | 
| +  char* target = NULL; | 
| gfc_typebound_proc* tb; | 
| bool seen_colons; | 
| bool seen_attrs; | 
| @@ -6867,11 +7431,25 @@ match_procedure_in_type (void) | 
| block = gfc_state_stack->previous->sym; | 
| gcc_assert (block); | 
|  | 
| -  /* TODO: Really implement PROCEDURE(interface).  */ | 
| +  /* Try to match PROCEDURE(interface).  */ | 
| if (gfc_match (" (") == MATCH_YES) | 
| { | 
| -      gfc_error ("PROCEDURE(interface) at %C is not yet implemented"); | 
| -      return MATCH_ERROR; | 
| +      m = gfc_match_name (target_buf); | 
| +      if (m == MATCH_ERROR) | 
| +	return m; | 
| +      if (m != MATCH_YES) | 
| +	{ | 
| +	  gfc_error ("Interface-name expected after '(' at %C"); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| +      if (gfc_match (" )") != MATCH_YES) | 
| +	{ | 
| +	  gfc_error ("')' expected at %C"); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| +      target = target_buf; | 
| } | 
|  | 
| /* Construct the data structure.  */ | 
| @@ -6880,11 +7458,24 @@ match_procedure_in_type (void) | 
| tb->is_generic = 0; | 
|  | 
| /* Match binding attributes.  */ | 
| -  m = match_binding_attributes (tb, false); | 
| +  m = match_binding_attributes (tb, false, false); | 
| if (m == MATCH_ERROR) | 
| return m; | 
| seen_attrs = (m == MATCH_YES); | 
|  | 
| +  /* Check that attribute DEFERRED is given iff an interface is specified, which | 
| +     means target != NULL.  */ | 
| +  if (tb->deferred && !target) | 
| +    { | 
| +      gfc_error ("Interface must be specified for DEFERRED binding at %C"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| +  if (target && !tb->deferred) | 
| +    { | 
| +      gfc_error ("PROCEDURE(interface) at %C should be declared DEFERRED"); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| /* Match the colons.  */ | 
| m = gfc_match (" ::"); | 
| if (m == MATCH_ERROR) | 
| @@ -6907,12 +7498,17 @@ match_procedure_in_type (void) | 
| } | 
|  | 
| /* Try to match the '=> target', if it's there.  */ | 
| -  target = NULL; | 
| m = gfc_match (" =>"); | 
| if (m == MATCH_ERROR) | 
| return m; | 
| if (m == MATCH_YES) | 
| { | 
| +      if (tb->deferred) | 
| +	{ | 
| +	  gfc_error ("'=> target' is invalid for DEFERRED binding at %C"); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| if (!seen_colons) | 
| { | 
| gfc_error ("'::' needed in PROCEDURE binding with explicit target" | 
| @@ -6949,11 +7545,19 @@ match_procedure_in_type (void) | 
| ns = block->f2k_derived; | 
| gcc_assert (ns); | 
|  | 
| +  /* If the binding is DEFERRED, check that the containing type is ABSTRACT.  */ | 
| +  if (tb->deferred && !block->attr.abstract) | 
| +    { | 
| +      gfc_error ("Type '%s' containing DEFERRED binding at %C is not ABSTRACT", | 
| +		 block->name); | 
| +      return MATCH_ERROR; | 
| +    } | 
| + | 
| /* See if we already have a binding with this name in the symtree which would | 
| be an error.  If a GENERIC already targetted this binding, it may be | 
| already there but then typebound is still NULL.  */ | 
| -  stree = gfc_find_symtree (ns->sym_root, name); | 
| -  if (stree && stree->typebound) | 
| +  stree = gfc_find_symtree (ns->tb_sym_root, name); | 
| +  if (stree && stree->n.tb) | 
| { | 
| gfc_error ("There's already a procedure with binding name '%s' for the" | 
| " derived type '%s' at %C", name, block->name); | 
| @@ -6961,12 +7565,17 @@ match_procedure_in_type (void) | 
| } | 
|  | 
| /* Insert it and set attributes.  */ | 
| -  if (gfc_get_sym_tree (name, ns, &stree)) | 
| -    return MATCH_ERROR; | 
| -  if (gfc_get_sym_tree (target, gfc_current_ns, &tb->u.specific)) | 
| + | 
| +  if (!stree) | 
| +    { | 
| +      stree = gfc_new_symtree (&ns->tb_sym_root, name); | 
| +      gcc_assert (stree); | 
| +    } | 
| +  stree->n.tb = tb; | 
| + | 
| +  if (gfc_get_sym_tree (target, gfc_current_ns, &tb->u.specific, false)) | 
| return MATCH_ERROR; | 
| gfc_set_sym_referenced (tb->u.specific->n.sym); | 
| -  stree->typebound = tb; | 
|  | 
| return MATCH_YES; | 
| } | 
| @@ -6978,11 +7587,13 @@ match | 
| gfc_match_generic (void) | 
| { | 
| char name[GFC_MAX_SYMBOL_LEN + 1]; | 
| +  char bind_name[GFC_MAX_SYMBOL_LEN + 16]; /* Allow space for OPERATOR(...).  */ | 
| gfc_symbol* block; | 
| gfc_typebound_proc tbattr; /* Used for match_binding_attributes.  */ | 
| gfc_typebound_proc* tb; | 
| -  gfc_symtree* st; | 
| gfc_namespace* ns; | 
| +  interface_type op_type; | 
| +  gfc_intrinsic_op op; | 
| match m; | 
|  | 
| /* Check current state.  */ | 
| @@ -6998,7 +7609,7 @@ gfc_match_generic (void) | 
| gcc_assert (block && ns); | 
|  | 
| /* See if we get an access-specifier.  */ | 
| -  m = match_binding_attributes (&tbattr, true); | 
| +  m = match_binding_attributes (&tbattr, true, false); | 
| if (m == MATCH_ERROR) | 
| goto error; | 
|  | 
| @@ -7009,47 +7620,126 @@ gfc_match_generic (void) | 
| goto error; | 
| } | 
|  | 
| -  /* The binding name and =>.  */ | 
| -  m = gfc_match (" %n =>", name); | 
| +  /* Match the binding name; depending on type (operator / generic) format | 
| +     it for future error messages into bind_name.  */ | 
| + | 
| +  m = gfc_match_generic_spec (&op_type, name, &op); | 
| if (m == MATCH_ERROR) | 
| return MATCH_ERROR; | 
| if (m == MATCH_NO) | 
| { | 
| -      gfc_error ("Expected generic name at %C"); | 
| +      gfc_error ("Expected generic name or operator descriptor at %C"); | 
| goto error; | 
| } | 
|  | 
| -  /* If there's already something with this name, check that it is another | 
| -     GENERIC and then extend that rather than build a new node.  */ | 
| -  st = gfc_find_symtree (ns->sym_root, name); | 
| -  if (st) | 
| +  switch (op_type) | 
| { | 
| -      if (!st->typebound || !st->typebound->is_generic) | 
| +    case INTERFACE_GENERIC: | 
| +      snprintf (bind_name, sizeof (bind_name), "%s", name); | 
| +      break; | 
| + | 
| +    case INTERFACE_USER_OP: | 
| +      snprintf (bind_name, sizeof (bind_name), "OPERATOR(.%s.)", name); | 
| +      break; | 
| + | 
| +    case INTERFACE_INTRINSIC_OP: | 
| +      snprintf (bind_name, sizeof (bind_name), "OPERATOR(%s)", | 
| +		gfc_op2string (op)); | 
| +      break; | 
| + | 
| +    default: | 
| +      gcc_unreachable (); | 
| +    } | 
| + | 
| +  /* Match the required =>.  */ | 
| +  if (gfc_match (" =>") != MATCH_YES) | 
| +    { | 
| +      gfc_error ("Expected '=>' at %C"); | 
| +      goto error; | 
| +    } | 
| + | 
| +  /* Try to find existing GENERIC binding with this name / for this operator; | 
| +     if there is something, check that it is another GENERIC and then extend | 
| +     it rather than building a new node.  Otherwise, create it and put it | 
| +     at the right position.  */ | 
| + | 
| +  switch (op_type) | 
| +    { | 
| +    case INTERFACE_USER_OP: | 
| +    case INTERFACE_GENERIC: | 
| +      { | 
| +	const bool is_op = (op_type == INTERFACE_USER_OP); | 
| +	gfc_symtree* st; | 
| + | 
| +	st = gfc_find_symtree (is_op ? ns->tb_uop_root : ns->tb_sym_root, name); | 
| +	if (st) | 
| +	  { | 
| +	    tb = st->n.tb; | 
| +	    gcc_assert (tb); | 
| +	  } | 
| +	else | 
| +	  tb = NULL; | 
| + | 
| +	break; | 
| +      } | 
| + | 
| +    case INTERFACE_INTRINSIC_OP: | 
| +      tb = ns->tb_op[op]; | 
| +      break; | 
| + | 
| +    default: | 
| +      gcc_unreachable (); | 
| +    } | 
| + | 
| +  if (tb) | 
| +    { | 
| +      if (!tb->is_generic) | 
| { | 
| +	  gcc_assert (op_type == INTERFACE_GENERIC); | 
| gfc_error ("There's already a non-generic procedure with binding name" | 
| " '%s' for the derived type '%s' at %C", | 
| -		     name, block->name); | 
| +		     bind_name, block->name); | 
| goto error; | 
| } | 
|  | 
| -      tb = st->typebound; | 
| if (tb->access != tbattr.access) | 
| { | 
| gfc_error ("Binding at %C must have the same access as already" | 
| -		     " defined binding '%s'", name); | 
| +		     " defined binding '%s'", bind_name); | 
| goto error; | 
| } | 
| } | 
| else | 
| { | 
| -      if (gfc_get_sym_tree (name, ns, &st)) | 
| -	return MATCH_ERROR; | 
| - | 
| -      st->typebound = tb = gfc_get_typebound_proc (); | 
| +      tb = gfc_get_typebound_proc (); | 
| tb->where = gfc_current_locus; | 
| tb->access = tbattr.access; | 
| tb->is_generic = 1; | 
| tb->u.generic = NULL; | 
| + | 
| +      switch (op_type) | 
| +	{ | 
| +	case INTERFACE_GENERIC: | 
| +	case INTERFACE_USER_OP: | 
| +	  { | 
| +	    const bool is_op = (op_type == INTERFACE_USER_OP); | 
| +	    gfc_symtree* st; | 
| + | 
| +	    st = gfc_new_symtree (is_op ? &ns->tb_uop_root : &ns->tb_sym_root, | 
| +				  name); | 
| +	    gcc_assert (st); | 
| +	    st->n.tb = tb; | 
| + | 
| +	    break; | 
| +	  } | 
| + | 
| +	case INTERFACE_INTRINSIC_OP: | 
| +	  ns->tb_op[op] = tb; | 
| +	  break; | 
| + | 
| +	default: | 
| +	  gcc_unreachable (); | 
| +	} | 
| } | 
|  | 
| /* Now, match all following names as specific targets.  */ | 
| @@ -7067,20 +7757,17 @@ gfc_match_generic (void) | 
| goto error; | 
| } | 
|  | 
| -      if (gfc_get_sym_tree (name, ns, &target_st)) | 
| -	goto error; | 
| +      target_st = gfc_get_tbp_symtree (&ns->tb_sym_root, name); | 
|  | 
| /* See if this is a duplicate specification.  */ | 
| for (target = tb->u.generic; target; target = target->next) | 
| if (target_st == target->specific_st) | 
| { | 
| gfc_error ("'%s' already defined as specific binding for the" | 
| -		       " generic '%s' at %C", name, st->n.sym->name); | 
| +		       " generic '%s' at %C", name, bind_name); | 
| goto error; | 
| } | 
|  | 
| -      gfc_set_sym_referenced (target_st->n.sym); | 
| - | 
| target = gfc_get_tbp_generic (); | 
| target->specific_st = target_st; | 
| target->specific = NULL; | 
| @@ -7115,8 +7802,18 @@ gfc_match_final_decl (void) | 
| bool first, last; | 
| gfc_symbol* block; | 
|  | 
| +  if (gfc_current_form == FORM_FREE) | 
| +    { | 
| +      char c = gfc_peek_ascii_char (); | 
| +      if (!gfc_is_whitespace (c) && c != ':') | 
| +	return MATCH_NO; | 
| +    } | 
| + | 
| if (gfc_state_stack->state != COMP_DERIVED_CONTAINS) | 
| { | 
| +      if (gfc_current_form == FORM_FIXED) | 
| +	return MATCH_NO; | 
| + | 
| gfc_error ("FINAL declaration at %C must be inside a derived type " | 
| "CONTAINS section"); | 
| return MATCH_ERROR; | 
| @@ -7208,3 +7905,101 @@ gfc_match_final_decl (void) | 
|  | 
| return MATCH_YES; | 
| } | 
| + | 
| + | 
| +const ext_attr_t ext_attr_list[] = { | 
| +  { "dllimport", EXT_ATTR_DLLIMPORT, "dllimport" }, | 
| +  { "dllexport", EXT_ATTR_DLLEXPORT, "dllexport" }, | 
| +  { "cdecl",     EXT_ATTR_CDECL,     "cdecl"     }, | 
| +  { "stdcall",   EXT_ATTR_STDCALL,   "stdcall"   }, | 
| +  { "fastcall",  EXT_ATTR_FASTCALL,  "fastcall"  }, | 
| +  { NULL,        EXT_ATTR_LAST,      NULL        } | 
| +}; | 
| + | 
| +/* Match a !GCC$ ATTRIBUTES statement of the form: | 
| +      !GCC$ ATTRIBUTES attribute-list :: var-name [, var-name] ... | 
| +   When we come here, we have already matched the !GCC$ ATTRIBUTES string. | 
| + | 
| +   TODO: We should support all GCC attributes using the same syntax for | 
| +   the attribute list, i.e. the list in C | 
| +      __attributes(( attribute-list )) | 
| +   matches then | 
| +      !GCC$ ATTRIBUTES attribute-list :: | 
| +   Cf. c-parser.c's c_parser_attributes; the data can then directly be | 
| +   saved into a TREE. | 
| + | 
| +   As there is absolutely no risk of confusion, we should never return | 
| +   MATCH_NO.  */ | 
| +match | 
| +gfc_match_gcc_attributes (void) | 
| +{ | 
| +  symbol_attribute attr; | 
| +  char name[GFC_MAX_SYMBOL_LEN + 1]; | 
| +  unsigned id; | 
| +  gfc_symbol *sym; | 
| +  match m; | 
| + | 
| +  gfc_clear_attr (&attr); | 
| +  for(;;) | 
| +    { | 
| +      char ch; | 
| + | 
| +      if (gfc_match_name (name) != MATCH_YES) | 
| +	return MATCH_ERROR; | 
| + | 
| +      for (id = 0; id < EXT_ATTR_LAST; id++) | 
| +	if (strcmp (name, ext_attr_list[id].name) == 0) | 
| +	  break; | 
| + | 
| +      if (id == EXT_ATTR_LAST) | 
| +	{ | 
| +	  gfc_error ("Unknown attribute in !GCC$ ATTRIBUTES statement at %C"); | 
| +	  return MATCH_ERROR; | 
| +	} | 
| + | 
| +      if (gfc_add_ext_attribute (&attr, (ext_attr_id_t) id, &gfc_current_locus) | 
| +	  == FAILURE) | 
| +	return MATCH_ERROR; | 
| + | 
| +      gfc_gobble_whitespace (); | 
| +      ch = gfc_next_ascii_char (); | 
| +      if (ch == ':') | 
| +        { | 
| +          /* This is the successful exit condition for the loop.  */ | 
| +          if (gfc_next_ascii_char () == ':') | 
| +            break; | 
| +        } | 
| + | 
| +      if (ch == ',') | 
| +	continue; | 
| + | 
| +      goto syntax; | 
| +    } | 
| + | 
| +  if (gfc_match_eos () == MATCH_YES) | 
| +    goto syntax; | 
| + | 
| +  for(;;) | 
| +    { | 
| +      m = gfc_match_name (name); | 
| +      if (m != MATCH_YES) | 
| +	return m; | 
| + | 
| +      if (find_special (name, &sym, true)) | 
| +	return MATCH_ERROR; | 
| + | 
| +      sym->attr.ext_attr |= attr.ext_attr; | 
| + | 
| +      if (gfc_match_eos () == MATCH_YES) | 
| +	break; | 
| + | 
| +      if (gfc_match_char (',') != MATCH_YES) | 
| +	goto syntax; | 
| +    } | 
| + | 
| +  return MATCH_YES; | 
| + | 
| +syntax: | 
| +  gfc_error ("Syntax error in !GCC$ ATTRIBUTES statement at %C"); | 
| +  return MATCH_ERROR; | 
| +} | 
|  |