General background: Private module variables and module procedures can be marked as TREE_PUBLIC()= 0, unless they are used in the specification expression of the dummy argument or result variable of public module procedures (or private module procedures in public generic interfaces).

That gives a lot of optimization possibilities. However, it is not trivial to get it right. The current version has resolve_function:

3128      if (sym && specification_expr && sym->attr.function
3129          && gfc_current_ns->proc_name
3130          && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
3131        sym->attr.public_used = 1;


That fails if one does not operate on a result variable but on a dummy argument, which might be not at ns->proc_name but at ns->parent->proc_name.

The attached patch tried to fix the 4.8 regression without breaking the existing test cases.

Build and regtested on x86-64-gnu-linux.
OK for the trunk?

Tobias

PS: I start to understand why other compilers don't do it.
2012-12-21  Tobias Burnus  <bur...@net-b.de>

	PR fortran/54884
	* resolve.c (spec_expr_mod_proc): New static variable.
	(resolve_formal_arglist, resolve_function, resolve_variable,
	resolve_charlen, resolve_fl_variable, resolve_symbol): Use
	it to decide when to mark a symbol as public_use.

2012-12-21  Tobias Burnus  <bur...@net-b.de>

	PR fortran/54884
	* gfortran.dg/public_private_module_8.f90: New.

diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c
index fce6f73..95cc4de 100644
--- a/gcc/fortran/resolve.c
+++ b/gcc/fortran/resolve.c
@@ -83,6 +83,10 @@ static int formal_arg_flag = 0;
 /* True if we are resolving a specification expression.  */
 static bool specification_expr = false;
 
+/* True if we are resolving the specification expression of a module
+   procedure's result or dummy variable; used for the public_use setting.  */
+static bool spec_expr_mod_proc = false;
+
 /* The id of the last entry seen.  */
 static int current_entry_id;
 
@@ -278,7 +282,7 @@ resolve_formal_arglist (gfc_symbol *proc)
 {
   gfc_formal_arglist *f;
   gfc_symbol *sym;
-  bool saved_specification_expr;
+  bool saved_specification_expr, saved_spec_expr_mod_proc;
   int i;
 
   if (proc->result != NULL)
@@ -339,8 +343,19 @@ resolve_formal_arglist (gfc_symbol *proc)
 
       saved_specification_expr = specification_expr;
       specification_expr = true;
+      saved_spec_expr_mod_proc = spec_expr_mod_proc;
+      if ((sym->attr.dummy || sym->attr.result || sym->attr.function)
+	  && ((sym == sym->result && sym->ns->proc_name
+	       && sym->ns->proc_name->attr.flavor == FL_MODULE)
+	      || (sym != sym->result && sym->ns->parent
+		  && sym->ns->parent->proc_name
+		  && sym->ns->parent->proc_name->attr.flavor == FL_MODULE)))
+	spec_expr_mod_proc = true;
+
       gfc_resolve_array_spec (as, 0);
+
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
 
       /* We can't tell if an array with dimension (:) is assumed or deferred
 	 shape until we know if it has the pointer or allocatable attributes.
@@ -3129,12 +3144,13 @@ resolve_function (gfc_expr *expr)
       return FAILURE;
     }
 
-  if (sym && specification_expr && sym->attr.function
-      && gfc_current_ns->proc_name
-      && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+  if (sym && spec_expr_mod_proc && sym->attr.function
+      && ((gfc_current_ns->proc_name
+	   && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+	  || (gfc_current_ns->parent && gfc_current_ns->parent->proc_name
+	      && gfc_current_ns->parent->proc_name->attr.flavor == FL_MODULE)))
     sym->attr.public_used = 1;
 
-
   /* Switch off assumed size checking and do this again for certain kinds
      of procedure, once the procedure itself is resolved.  */
   need_full_assumed_size++;
@@ -5363,14 +5379,15 @@ resolve_variable (gfc_expr *e)
   /* If a PRIVATE variable is used in the specification expression of the
      result variable, it might be accessed from outside the module and can
      thus not be TREE_PUBLIC() = 0.
-     TODO: sym->attr.public_used only has to be set for the result variable's
-     type-parameter expression and not for dummies or automatic variables.
-     Additionally, it only has to be set if the function is either PUBLIC or
-     used in a generic interface or TBP; unfortunately,
+     TODO: sym->attr.public_used only has to be set if the function is
+     either PUBLIC or used in a generic interface or TBP; unfortunately,
      proc_name->attr.public_used can get set at a later stage.  */
-  if (specification_expr && sym->attr.access == ACCESS_PRIVATE
+  if (spec_expr_mod_proc
       && !sym->attr.function && !sym->attr.use_assoc
-      && gfc_current_ns->proc_name && gfc_current_ns->proc_name->attr.function)
+      && ((gfc_current_ns->proc_name
+	   && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+	  || (gfc_current_ns->parent && gfc_current_ns->parent->proc_name
+	      && gfc_current_ns->parent->proc_name->attr.flavor == FL_MODULE)))
     sym->attr.public_used = 1;
 
   /* Deal with forward references to entries during resolve_code, to
@@ -5384,7 +5401,7 @@ resolve_variable (gfc_expr *e)
       gfc_entry_list *entry;
       gfc_formal_arglist *formal;
       int n;
-      bool seen, saved_specification_expr;
+      bool seen, saved_specification_expr, saved_spec_expr_mod_proc;
 
       /* If the symbol is a dummy...  */
       if (sym->attr.dummy && sym->ns == gfc_current_ns)
@@ -5419,6 +5436,15 @@ resolve_variable (gfc_expr *e)
       /* Now do the same check on the specification expressions.  */
       saved_specification_expr = specification_expr;
       specification_expr = true;
+      saved_spec_expr_mod_proc = spec_expr_mod_proc;
+      if ((sym->attr.dummy || sym->attr.result || sym->attr.function)
+	  && ((sym == sym->result && sym->ns->proc_name
+	       && sym->ns->proc_name->attr.flavor == FL_MODULE)
+	      || (sym != sym->result && sym->ns->parent
+		  && sym->ns->parent->proc_name
+		  && sym->ns->parent->proc_name->attr.flavor == FL_MODULE)))
+	spec_expr_mod_proc = true;
+
       if (sym->ts.type == BT_CHARACTER
 	  && gfc_resolve_expr (sym->ts.u.cl->length) == FAILURE)
 	t = FAILURE;
@@ -5432,6 +5458,7 @@ resolve_variable (gfc_expr *e)
 	       t = FAILURE;
 	  }
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
 
       if (t == SUCCESS)
 	/* Update the symbol's entry level.  */
@@ -10674,7 +10701,7 @@ static gfc_try
 resolve_charlen (gfc_charlen *cl)
 {
   int i, k;
-  bool saved_specification_expr;
+  bool saved_specification_expr, saved_spec_expr_mod_proc;
 
   if (cl->resolved)
     return SUCCESS;
@@ -10682,18 +10709,26 @@ resolve_charlen (gfc_charlen *cl)
   cl->resolved = 1;
   saved_specification_expr = specification_expr;
   specification_expr = true;
+  saved_spec_expr_mod_proc = spec_expr_mod_proc;
+  if ((gfc_current_ns->proc_name
+       && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+      || (gfc_current_ns->parent && gfc_current_ns->parent->proc_name
+	  && gfc_current_ns->parent->proc_name->attr.flavor == FL_MODULE))
+    spec_expr_mod_proc = true;
 
   if (cl->length_from_typespec)
     {
       if (gfc_resolve_expr (cl->length) == FAILURE)
 	{
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
 
       if (gfc_simplify_expr (cl->length, 0) == FAILURE)
 	{
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
     }
@@ -10703,6 +10738,7 @@ resolve_charlen (gfc_charlen *cl)
       if (resolve_index_expr (cl->length) == FAILURE)
 	{
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
     }
@@ -10727,10 +10763,12 @@ resolve_charlen (gfc_charlen *cl)
     {
       gfc_error ("String length at %L is too large", &cl->length->where);
       specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
       return FAILURE;
     }
 
   specification_expr = saved_specification_expr;
+  spec_expr_mod_proc = saved_spec_expr_mod_proc;
   return SUCCESS;
 }
 
@@ -11192,7 +11230,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
   int no_init_flag, automatic_flag;
   gfc_expr *e;
   const char *auto_save_msg;
-  bool saved_specification_expr;
+  bool saved_specification_expr, saved_spec_expr_mod_proc;
 
   auto_save_msg = "Automatic object '%s' at %L cannot have the "
 		  "SAVE attribute";
@@ -11205,6 +11243,13 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
      is_non_constant_shape_array.  */
   saved_specification_expr = specification_expr;
   specification_expr = true;
+  saved_spec_expr_mod_proc = spec_expr_mod_proc;
+  if ((sym->attr.dummy || sym->attr.result || sym->attr.function)
+      && ((gfc_current_ns->proc_name
+	   && gfc_current_ns->proc_name->attr.flavor == FL_MODULE)
+	  || (gfc_current_ns->parent && gfc_current_ns->parent->proc_name
+	  && gfc_current_ns->parent->proc_name->attr.flavor == FL_MODULE)))
+    spec_expr_mod_proc = true;
 
   if (sym->ns->proc_name
       && (sym->ns->proc_name->attr.flavor == FL_MODULE
@@ -11219,6 +11264,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
       gfc_error ("The module or main program array '%s' at %L must "
 		 "have constant shape", sym->name, &sym->declared_at);
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
       return FAILURE;
     }
 
@@ -11229,6 +11275,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 		 "requires either the pointer or allocatable attribute",
 		     sym->name, &sym->declared_at);
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
       return FAILURE;
     }
 
@@ -11243,6 +11290,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 	  gfc_error ("Entity with assumed character length at %L must be a "
 		     "dummy argument or a PARAMETER", &sym->declared_at);
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
 
@@ -11250,6 +11298,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 	{
 	  gfc_error (auto_save_msg, sym->name, &sym->declared_at);
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
 
@@ -11264,6 +11313,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 	      gfc_error ("'%s' at %L must have constant character length "
 			"in this context", sym->name, &sym->declared_at);
 	      specification_expr = saved_specification_expr;
+	      spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	      return FAILURE;
 	    }
 	  if (sym->attr.in_common)
@@ -11271,6 +11321,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 	      gfc_error ("COMMON variable '%s' at %L must have constant "
 			 "character length", sym->name, &sym->declared_at);
 	      specification_expr = saved_specification_expr;
+	      spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	      return FAILURE;
 	    }
 	}
@@ -11302,6 +11353,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
 	{
 	  gfc_error (auto_save_msg, sym->name, &sym->declared_at);
 	  specification_expr = saved_specification_expr;
+	  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 	  return FAILURE;
 	}
     }
@@ -11336,6 +11388,7 @@ resolve_fl_variable (gfc_symbol *sym, int mp_flag)
       else
 	goto no_init_error;
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
       return FAILURE;
     }
 
@@ -11344,10 +11397,12 @@ no_init_error:
     {
       gfc_try res = resolve_fl_variable_derived (sym, no_init_flag);
       specification_expr = saved_specification_expr;
+      spec_expr_mod_proc = saved_spec_expr_mod_proc;
       return res;
     }
 
   specification_expr = saved_specification_expr;
+  spec_expr_mod_proc = saved_spec_expr_mod_proc;
   return SUCCESS;
 }
 
@@ -13151,7 +13206,7 @@ resolve_symbol (gfc_symbol *sym)
   gfc_component *c;
   symbol_attribute class_attr;
   gfc_array_spec *as;
-  bool saved_specification_expr;
+  bool saved_specification_expr, saved_spec_expr_mod_proc;
 
   if (sym->attr.artificial)
     return;
@@ -13699,8 +13754,19 @@ resolve_symbol (gfc_symbol *sym)
 
   saved_specification_expr = specification_expr;
   specification_expr = true;
+  saved_spec_expr_mod_proc = spec_expr_mod_proc;
+  if ((sym->attr.dummy || sym->attr.result || sym->attr.function)
+       && ((sym == sym->result && sym->ns->proc_name
+       && sym->ns->proc_name->attr.flavor == FL_MODULE)
+      || (sym != sym->result && sym->ns->parent
+	  && sym->ns->parent->proc_name
+	  && sym->ns->parent->proc_name->attr.flavor == FL_MODULE)))
+    spec_expr_mod_proc = true;
+
   gfc_resolve_array_spec (sym->as, check_constant);
+
   specification_expr = saved_specification_expr;
+  spec_expr_mod_proc = saved_spec_expr_mod_proc;
 
   formal_arg_flag = 0;
 
diff --git a/gcc/testsuite/gfortran.dg/public_private_module_8.f90 b/gcc/testsuite/gfortran.dg/public_private_module_8.f90
new file mode 100644
index 0000000..8543320
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/public_private_module_8.f90
@@ -0,0 +1,52 @@
+! { dg-do compile }
+! { dg-options "-O2" }
+!
+! PR fortran/54884
+!
+! Check that get_key_len is not optimized away as it
+! is used in a publicly visible specification expression.
+!
+
+module m
+  private
+  public :: foo
+  interface foo
+    module procedure bar
+  end interface foo
+contains
+  pure function mylen()
+    integer :: mylen
+    mylen = 42
+  end function mylen
+  pure function myotherlen()
+    integer :: myotherlen
+    myotherlen = 99
+  end function myotherlen
+  subroutine bar(x)
+    character(len=mylen()) :: x
+    character :: z2(myotherlen())
+    call internal(x)
+    block
+       character(len=myotherlen()) :: z
+       z = "abc"
+       x(1:5) = z
+    end block
+!    x(6:10) = intern_func()
+  contains
+! The following currently fails as character lengths are
+! resolved separately; additionally intern_func's
+! sym->ns->proc_name is "bar".
+!    function intern_func()
+!      character(len=myotherlen()) :: intern_func
+!      intern_func = "zuzu"
+!    end function intern_func
+    subroutine internal(y)
+      character(len=myotherlen()) :: y
+      y = "abc"
+    end subroutine internal
+  end subroutine bar
+end module m
+
+! { dg-final { scan-assembler-not "__m_MOD_myotherlen" } }
+! { dg-final { scan-assembler "__m_MOD_bar" } }
+! { dg-final { scan-assembler "__m_MOD_mylen" } }

Reply via email to