OSDN Git Service

* vec.h: Comment improvements.
[pf3gnuchains/gcc-fork.git] / gcc / fortran / trans-array.c
index 731fb19..46bf211 100644 (file)
@@ -288,27 +288,26 @@ gfc_conv_descriptor_ubound (tree desc, tree dim)
 }
 
 
-/* Generate an initializer for a static pointer or allocatable array.  */
+/* Build an null array descriptor constructor.  */
 
-void
-gfc_trans_static_array_pointer (gfc_symbol * sym)
+tree
+gfc_build_null_descriptor (tree type)
 {
-  tree tmp;
   tree field;
-  tree type;
+  tree tmp;
 
-  assert (TREE_STATIC (sym->backend_decl));
-  /* Just zero the data member.  */
-  type = TREE_TYPE (sym->backend_decl);
   assert (GFC_DESCRIPTOR_TYPE_P (type));
   assert (DATA_FIELD == 0);
   field = TYPE_FIELDS (type);
 
+  /* Set a NULL data pointer.  */
   tmp = tree_cons (field, null_pointer_node, NULL_TREE);
   tmp = build1 (CONSTRUCTOR, type, tmp);
   TREE_CONSTANT (tmp) = 1;
   TREE_INVARIANT (tmp) = 1;
-  DECL_INITIAL (sym->backend_decl) = tmp;
+  /* All other fields are ignored.  */
+
+  return tmp;
 }
 
 
@@ -422,8 +421,24 @@ gfc_add_ss_to_loop (gfc_loopinfo * loop, gfc_ss * head)
 }
 
 
+/* Generate an initializer for a static pointer or allocatable array.  */
+
+void
+gfc_trans_static_array_pointer (gfc_symbol * sym)
+{
+  tree type;
+
+  assert (TREE_STATIC (sym->backend_decl));
+  /* Just zero the data member.  */
+  type = TREE_TYPE (sym->backend_decl);
+  DECL_INITIAL (sym->backend_decl) =gfc_build_null_descriptor (type);
+}
+
+
 /* Generate code to allocate an array temporary, or create a variable to
-   hold the data.  */
+   hold the data.  If size is NULL zero the descriptor so that so that the
+   callee will allocate the array.  Also generates code to free the array
+   afterwards.  */
 
 static void
 gfc_trans_allocate_array_storage (gfc_loopinfo * loop, gfc_ss_info * info,
@@ -437,38 +452,54 @@ gfc_trans_allocate_array_storage (gfc_loopinfo * loop, gfc_ss_info * info,
 
   desc = info->descriptor;
   data = gfc_conv_descriptor_data (desc);
-  onstack = gfc_can_put_var_on_stack (size);
-  if (onstack)
+  if (size == NULL_TREE)
     {
-      /* Make a temporary variable to hold the data.  */
-      tmp = fold (build (MINUS_EXPR, TREE_TYPE (nelem), nelem,
-                        integer_one_node));
-      tmp = build_range_type (gfc_array_index_type, gfc_index_zero_node, tmp);
-      tmp = build_array_type (gfc_get_element_type (TREE_TYPE (desc)), tmp);
-      tmp = gfc_create_var (tmp, "A");
-      tmp = gfc_build_addr_expr (TREE_TYPE (data), tmp);
-      gfc_add_modify_expr (&loop->pre, data, tmp);
+      /* A callee allocated array.  */
+      gfc_add_modify_expr (&loop->pre, data, convert (TREE_TYPE (data), 
+                                                      gfc_index_zero_node));
       info->data = data;
       info->offset = gfc_index_zero_node;
-
+      onstack = FALSE;
     }
   else
     {
-      /* Allocate memory to hold the data.  */
-      args = gfc_chainon_list (NULL_TREE, size);
+      /* Allocate the temporary.  */
+      onstack = gfc_can_put_var_on_stack (size);
+
+      if (onstack)
+       {
+         /* Make a temporary variable to hold the data.  */
+         tmp = fold (build (MINUS_EXPR, TREE_TYPE (nelem), nelem,
+                            integer_one_node));
+         tmp = build_range_type (gfc_array_index_type, gfc_index_zero_node,
+                                 tmp);
+         tmp = build_array_type (gfc_get_element_type (TREE_TYPE (desc)),
+                                 tmp);
+         tmp = gfc_create_var (tmp, "A");
+         tmp = gfc_build_addr_expr (TREE_TYPE (data), tmp);
+         gfc_add_modify_expr (&loop->pre, data, tmp);
+         info->data = data;
+         info->offset = gfc_index_zero_node;
 
-      if (gfc_index_integer_kind == 4)
-       tmp = gfor_fndecl_internal_malloc;
-      else if (gfc_index_integer_kind == 8)
-       tmp = gfor_fndecl_internal_malloc64;
+       }
       else
-       abort ();
-      tmp = gfc_build_function_call (tmp, args);
-      tmp = convert (TREE_TYPE (data), tmp);
-      gfc_add_modify_expr (&loop->pre, data, tmp);
+       {
+         /* Allocate memory to hold the data.  */
+         args = gfc_chainon_list (NULL_TREE, size);
 
-      info->data = data;
-      info->offset = gfc_index_zero_node;
+         if (gfc_index_integer_kind == 4)
+           tmp = gfor_fndecl_internal_malloc;
+         else if (gfc_index_integer_kind == 8)
+           tmp = gfor_fndecl_internal_malloc64;
+         else
+           abort ();
+         tmp = gfc_build_function_call (tmp, args);
+         tmp = convert (TREE_TYPE (data), tmp);
+         gfc_add_modify_expr (&loop->pre, data, tmp);
+
+         info->data = data;
+         info->offset = gfc_index_zero_node;
+       }
     }
 
   /* The offset is zero because we create temporaries with a zero
@@ -488,9 +519,11 @@ gfc_trans_allocate_array_storage (gfc_loopinfo * loop, gfc_ss_info * info,
 
 
 /* Generate code to allocate and initialize the descriptor for a temporary
-   array.  Fills in the descriptor, data and offset fields of info.  Also
-   adjusts the loop variables to be zero-based.  Returns the size of the
-   array.  */
+   array.  This is used for both temporaries needed by the scaparizer, and
+   functions returning arrays.  Adjusts the loop variables to be zero-based,
+   and calculates the loop bounds for callee allocated arrays.
+   Also fills in the descriptor, data and offset fields of info if known.
+   Returns the size of the array, or NULL for a callee allocated array.  */
 
 tree
 gfc_trans_allocate_temp_array (gfc_loopinfo * loop, gfc_ss_info * info,
@@ -513,7 +546,9 @@ gfc_trans_allocate_temp_array (gfc_loopinfo * loop, gfc_ss_info * info,
        assert (integer_zerop (loop->from[n]));
       else
        {
-         loop->to[n] = fold (build (MINUS_EXPR, gfc_array_index_type,
+         /* Callee allocated arrays may not have a known bound yet.  */
+          if (loop->to[n])
+              loop->to[n] = fold (build (MINUS_EXPR, gfc_array_index_type,
                                     loop->to[n], loop->from[n]));
          loop->from[n] = gfc_index_zero_node;
        }
@@ -553,6 +588,18 @@ gfc_trans_allocate_temp_array (gfc_loopinfo * loop, gfc_ss_info * info,
 
   for (n = 0; n < info->dimen; n++)
     {
+      if (loop->to[n] == NULL_TREE)
+        {
+         /* For a callee allocated array express the loop bounds in terms
+            of the descriptor fields.  */
+          tmp = build (MINUS_EXPR, gfc_array_index_type,
+                       gfc_conv_descriptor_ubound (desc, gfc_rank_cst[n]),
+                       gfc_conv_descriptor_lbound (desc, gfc_rank_cst[n]));
+          loop->to[n] = tmp;
+          size = NULL_TREE;
+          continue;
+        }
+        
       /* Store the stride and bound components in the descriptor.  */
       tmp = gfc_conv_descriptor_stride (desc, gfc_rank_cst[n]);
       gfc_add_modify_expr (&loop->pre, tmp, size);
@@ -576,7 +623,8 @@ gfc_trans_allocate_temp_array (gfc_loopinfo * loop, gfc_ss_info * info,
 
   /* Get the size of the array.  */
   nelem = size;
-  size = fold (build (MULT_EXPR, gfc_array_index_type, size,
+  if (size)
+    size = fold (build (MULT_EXPR, gfc_array_index_type, size,
                      TYPE_SIZE_UNIT (gfc_get_element_type (type))));
 
   gfc_trans_allocate_array_storage (loop, info, size, nelem);
@@ -972,7 +1020,6 @@ gfc_trans_array_constructor (gfc_loopinfo * loop, gfc_ss * ss)
 /* Add the pre and post chains for all the scalar expressions in a SS chain
    to loop.  This is called after the loop parameters have been calculated,
    but before the actual scalarizing loops.  */
-/*GCC ARRAYS*/
 
 static void
 gfc_add_loop_ss_code (gfc_loopinfo * loop, gfc_ss * ss, bool subscript)
@@ -980,6 +1027,8 @@ gfc_add_loop_ss_code (gfc_loopinfo * loop, gfc_ss * ss, bool subscript)
   gfc_se se;
   int n;
 
+  /* TODO: This can generate bad code if there are ordering dependencies.
+     eg. a callee allocated function and an unknown size constructor.  */
   assert (ss != NULL);
 
   for (; ss != gfc_ss_terminator; ss = ss->loop_chain)
@@ -1052,6 +1101,11 @@ gfc_add_loop_ss_code (gfc_loopinfo * loop, gfc_ss * ss, bool subscript)
          gfc_trans_array_constructor (loop, ss);
          break;
 
+        case GFC_SS_TEMP:
+       case GFC_SS_COMPONENT:
+          /* Do nothing.  These are handled elsewhere.  */
+          break;
+
        default:
          abort ();
        }
@@ -1395,9 +1449,12 @@ gfc_conv_array_index_offset (gfc_se * se, gfc_ss_info * info, int dim, int i,
     }
   else
     {
-      /* Temporary array.  */
+      /* Temporary array or derived type component.  */
       assert (se->loop);
       index = se->loop->loopvar[se->loop->order[i]];
+      if (!integer_zerop (info->delta[i]))
+       index = fold (build (PLUS_EXPR, gfc_array_index_type, index,
+                            info->delta[i]));
     }
 
   /* Multiply by the stride.  */
@@ -1546,7 +1603,8 @@ gfc_trans_preloop_setup (gfc_loopinfo * loop, int dim, int flag,
        continue;
 
       if (ss->type != GFC_SS_SECTION
-         && ss->type != GFC_SS_FUNCTION && ss->type != GFC_SS_CONSTRUCTOR)
+         && ss->type != GFC_SS_FUNCTION && ss->type != GFC_SS_CONSTRUCTOR
+         && ss->type != GFC_SS_COMPONENT)
        continue;
 
       info = &ss->data.info;
@@ -1768,7 +1826,8 @@ gfc_trans_scalarized_loop_boundary (gfc_loopinfo * loop, stmtblock_t * body)
        continue;
 
       if (ss->type != GFC_SS_SECTION
-         && ss->type != GFC_SS_FUNCTION && ss->type != GFC_SS_CONSTRUCTOR)
+         && ss->type != GFC_SS_FUNCTION && ss->type != GFC_SS_CONSTRUCTOR
+         && ss->type != GFC_SS_COMPONENT)
        continue;
 
       ss->data.info.offset = ss->data.info.saved_offset;
@@ -1924,6 +1983,7 @@ gfc_conv_ss_startstride (gfc_loopinfo * loop)
        case GFC_SS_SECTION:
        case GFC_SS_CONSTRUCTOR:
        case GFC_SS_FUNCTION:
+       case GFC_SS_COMPONENT:
          loop->dimen = ss->data.info.dimen;
          break;
 
@@ -1939,6 +1999,9 @@ gfc_conv_ss_startstride (gfc_loopinfo * loop)
   /* Loop over all the SS in the chain.  */
   for (ss = loop->ss; ss != gfc_ss_terminator; ss = ss->loop_chain)
     {
+      if (ss->expr && ss->expr->shape && !ss->shape)
+       ss->shape = ss->expr->shape;
+
       switch (ss->type)
        {
        case GFC_SS_SECTION:
@@ -2220,7 +2283,7 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
          loop for this dimension.  We try to pick the simplest term.  */
       for (ss = loop->ss; ss != gfc_ss_terminator; ss = ss->loop_chain)
        {
-         if (ss->expr && ss->expr->shape)
+         if (ss->shape)
            {
              /* The frontend has worked out the size for us.  */
              loopspec[n] = ss;
@@ -2229,6 +2292,10 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
 
          if (ss->type == GFC_SS_CONSTRUCTOR)
            {
+             /* An unknown size constructor will always be rank one.
+                Higher rank constructors will wither have known shape,
+                or still be wrapped in a call to reshape.  */
+             assert (loop->dimen == 1);
              /* Try to figure out the size of the constructor.  */
              /* TODO: avoid this by making the frontend set the shape.  */
              gfc_get_array_cons_size (&i, ss->expr->value.constructor);
@@ -2243,13 +2310,17 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
              continue;
            }
 
-         /* We don't know how to handle functions yet.
-            This may not be possible in all cases.  */
+         /* TODO: Pick the best bound if we have a choice between a
+            function and something else.  */
+          if (ss->type == GFC_SS_FUNCTION)
+            {
+              loopspec[n] = ss;
+              continue;
+            }
+
          if (ss->type != GFC_SS_SECTION)
            continue;
 
-         info = &ss->data.info;
-
          if (loopspec[n])
            specinfo = &loopspec[n]->data.info;
          else
@@ -2264,6 +2335,7 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
           */
          if (!specinfo)
            loopspec[n] = ss;
+         /* TODO: Is != contructor correct?  */
          else if (loopspec[n]->type != GFC_SS_CONSTRUCTOR)
            {
              if (integer_onep (info->stride[n])
@@ -2288,7 +2360,7 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
       info = &loopspec[n]->data.info;
 
       /* Set the extents of this range.  */
-      cshape = loopspec[n]->expr->shape;
+      cshape = loopspec[n]->shape;
       if (cshape && INTEGER_CST_P (info->start[n])
          && INTEGER_CST_P (info->stride[n]))
        {
@@ -2320,6 +2392,11 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
                                                          &loop->pre);
              break;
 
+            case GFC_SS_FUNCTION:
+             /* The loop bound will be set when we generate the call.  */
+              assert (loop->to[n] == NULL_TREE);
+              break;
+
            default:
              abort ();
            }
@@ -2346,6 +2423,11 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
        }
     }
 
+  /* Add all the scalar code that can be taken out of the loops.
+     This may include calculating the loop bounds, so do it before
+     allocating the temporary.  */
+  gfc_add_loop_ss_code (loop, loop->ss, false);
+
   /* If we want a temporary then create it.  */
   if (loop->temp_ss != NULL)
     {
@@ -2360,9 +2442,6 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
                                     tmp, len);
     }
 
-  /* Add all the scalar code that can be taken out of the loops.  */
-  gfc_add_loop_ss_code (loop, loop->ss, false);
-
   for (n = 0; n < loop->temp_dim; n++)
     loopspec[loop->order[n]] = NULL;
 
@@ -2376,7 +2455,7 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
   /* Calculate the translation from loop variables to array indices.  */
   for (ss = loop->ss; ss != gfc_ss_terminator; ss = ss->loop_chain)
     {
-      if (ss->type != GFC_SS_SECTION)
+      if (ss->type != GFC_SS_SECTION && ss->type != GFC_SS_COMPONENT)
        continue;
 
       info = &ss->data.info;
@@ -2385,7 +2464,7 @@ gfc_conv_loop_setup (gfc_loopinfo * loop)
        {
          dim = info->dim[n];
 
-         /* If we are specifying the range the delta may already be set.  */
+         /* If we are specifying the range the delta is already set.  */
          if (loopspec[n] != ss)
            {
              /* Calculate the offset relative to the loop variable.
@@ -2641,7 +2720,11 @@ gfc_conv_array_initializer (tree type, gfc_expr * expr)
       /* A single scalar or derived type value.  Create an array with all
          elements equal to that value.  */
       gfc_init_se (&se, NULL);
-      gfc_conv_expr (&se, expr);
+      
+      if (expr->expr_type == EXPR_CONSTANT)
+       gfc_conv_constant (&se, expr);
+      else
+       gfc_conv_structure (&se, expr, 1);
 
       tmp = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
       assert (tmp && INTEGER_CST_P (tmp));
@@ -2822,7 +2905,6 @@ gfc_trans_auto_array_allocation (tree decl, gfc_symbol * sym, tree fnbody)
   tree fndecl;
   tree size;
   tree offset;
-  tree args;
   bool onstack;
 
   assert (!(sym->attr.pointer || sym->attr.allocatable));
@@ -2835,20 +2917,6 @@ gfc_trans_auto_array_allocation (tree decl, gfc_symbol * sym, tree fnbody)
   assert (GFC_ARRAY_TYPE_P (type));
   onstack = TREE_CODE (type) != POINTER_TYPE;
 
-  /* We never generate initialization code of module variables.  */
-  if (fnbody == NULL_TREE)
-    {
-      assert (onstack);
-
-      /* Generate static initializer.  */
-      if (sym->value)
-       {
-         DECL_INITIAL (decl) =
-           gfc_conv_array_initializer (TREE_TYPE (decl), sym->value);
-       }
-      return fnbody;
-    }
-
   gfc_start_block (&block);
 
   /* Evaluate character string length.  */
@@ -2857,26 +2925,14 @@ gfc_trans_auto_array_allocation (tree decl, gfc_symbol * sym, tree fnbody)
     {
       gfc_trans_init_string_length (sym->ts.cl, &block);
 
-      DECL_DEFER_OUTPUT (decl) = 1;
-
-      /* Generate code to allocate the automatic variable.  It will be
-        freed automatically.  */
-      tmp = gfc_build_addr_expr (NULL, decl);
-      args = gfc_chainon_list (NULL_TREE, tmp);
-      args = gfc_chainon_list (args, sym->ts.cl->backend_decl);
-      tmp = gfc_build_function_call (built_in_decls[BUILT_IN_STACK_ALLOC],
-                                    args);
+      /* Emit a DECL_EXPR for this variable, which will cause the
+        gimplifier to allocate stoage, and all that good stuff.  */
+      tmp = build (DECL_EXPR, TREE_TYPE (decl), decl);
       gfc_add_expr_to_block (&block, tmp);
     }
 
   if (onstack)
     {
-      if (sym->value)
-       {
-         DECL_INITIAL (decl) =
-           gfc_conv_array_initializer (TREE_TYPE (decl), sym->value);
-       }
-
       gfc_add_expr_to_block (&block, fnbody);
       return gfc_finish_block (&block);
     }
@@ -2954,7 +3010,7 @@ gfc_trans_g77_array (gfc_symbol * sym, tree body)
   gfc_start_block (&block);
 
   if (sym->ts.type == BT_CHARACTER
-      && !INTEGER_CST_P (sym->ts.cl->backend_decl))
+      && TREE_CODE (sym->ts.cl->backend_decl) == VAR_DECL)
     gfc_trans_init_string_length (sym->ts.cl, &block);
 
   /* Evaluate the bounds of the array.  */
@@ -3019,6 +3075,10 @@ gfc_trans_dummy_array_bias (gfc_symbol * sym, tree tmpdesc, tree body)
   int checkparm;
   int no_repack;
 
+  /* Do nothing for pointer and allocatable arrays.  */
+  if (sym->attr.pointer || sym->attr.allocatable)
+    return body;
+
   if (sym->attr.dummy && gfc_is_nodesc_array (sym))
     return gfc_trans_g77_array (sym, body);
 
@@ -3033,7 +3093,7 @@ gfc_trans_dummy_array_bias (gfc_symbol * sym, tree tmpdesc, tree body)
   gfc_start_block (&block);
 
   if (sym->ts.type == BT_CHARACTER
-      && !INTEGER_CST_P (sym->ts.cl->backend_decl))
+      && TREE_CODE (sym->ts.cl->backend_decl) == VAR_DECL)
     gfc_trans_init_string_length (sym->ts.cl, &block);
 
   checkparm = (sym->as->type == AS_EXPLICIT && flag_bounds_check);
@@ -3291,15 +3351,17 @@ gfc_conv_expr_descriptor (gfc_se * se, gfc_expr * expr, gfc_ss * ss)
   tree start;
   tree offset;
   int full;
+  gfc_ss *vss;
 
   assert (ss != gfc_ss_terminator);
 
   /* TODO: Pass constant array constructors without a temporary.  */
-  /* If we have a linear array section, we can pass it directly.  Otherwise
-     we need to copy it into a temporary.  */
-  if (expr->expr_type == EXPR_VARIABLE)
+  /* Special case things we know we can pass easily.  */
+  switch (expr->expr_type)
     {
-      gfc_ss *vss;
+    case EXPR_VARIABLE:
+      /* If we have a linear array section, we can pass it directly.
+        Otherwise we need to copy it into a temporary.  */
 
       /* Find the SS for the array section.  */
       secss = ss;
@@ -3359,23 +3421,64 @@ gfc_conv_expr_descriptor (gfc_se * se, gfc_expr * expr, gfc_ss * ss)
          else if (se->want_pointer)
            {
              /* We pass full arrays directly.  This means that pointers and
-                allocatable arrays should also work.  */
-             se->expr = gfc_build_addr_expr (NULL, desc);
+                allocatable arrays should also work.  */
+             se->expr = gfc_build_addr_expr (NULL_TREE, desc);
            }
          else
            {
              se->expr = desc;
            }
+         if (expr->ts.type == BT_CHARACTER)
+           se->string_length = expr->symtree->n.sym->ts.cl->backend_decl;
          return;
        }
-    }
-  else
-    {
+      break;
+      
+    case EXPR_FUNCTION:
+      /* A transformational function return value will be a temporary
+        array descriptor.  We still need to go through the scalarizer
+        to create the descriptor.  Elemental functions ar handled as
+        arbitary expressions, ie. copy to a temporary.  */
+      secss = ss;
+      /* Look for the SS for this function.  */
+      while (secss != gfc_ss_terminator
+            && (secss->type != GFC_SS_FUNCTION || secss->expr != expr))
+       secss = secss->next;
+
+      if (se->direct_byref)
+       {
+         assert (secss != gfc_ss_terminator);
+
+         /* For pointer assignments pass the descriptor directly.  */
+         se->ss = secss;
+         se->expr = gfc_build_addr_expr (NULL, se->expr);
+         gfc_conv_expr (se, expr);
+         return;
+       }
+
+      if (secss == gfc_ss_terminator)
+       {
+         /* Elemental function.  */
+         need_tmp = 1;
+         info = NULL;
+       }
+      else
+       {
+         /* Transformational function.  */
+         info = &secss->data.info;
+         need_tmp = 0;
+       }
+      break;
+
+    default:
+      /* Something complicated.  Copy it into a temporary.  */
       need_tmp = 1;
       secss = NULL;
       info = NULL;
+      break;
     }
 
+
   gfc_init_loopinfo (&loop);
 
   /* Associate the SS with the loop.  */
@@ -3397,7 +3500,12 @@ gfc_conv_expr_descriptor (gfc_se * se, gfc_expr * expr, gfc_ss * ss)
       loop.temp_ss->type = GFC_SS_TEMP;
       loop.temp_ss->next = gfc_ss_terminator;
       loop.temp_ss->data.temp.type = gfc_typenode_for_spec (&expr->ts);
-      loop.temp_ss->data.temp.string_length = NULL;
+      /* Which can hold our string, if present.  */
+      if (expr->ts.type == BT_CHARACTER)
+       se->string_length = loop.temp_ss->data.temp.string_length
+         = TYPE_SIZE_UNIT (loop.temp_ss->data.temp.type);
+      else
+       loop.temp_ss->data.temp.string_length = NULL;
       loop.temp_ss->data.temp.dimen = loop.dimen;
       gfc_add_ss_to_loop (&loop, loop.temp_ss);
     }
@@ -3445,11 +3553,25 @@ gfc_conv_expr_descriptor (gfc_se * se, gfc_expr * expr, gfc_ss * ss)
       assert (is_gimple_lvalue (desc));
       se->expr = gfc_build_addr_expr (NULL, desc);
     }
+  else if (expr->expr_type == EXPR_FUNCTION)
+    {
+      desc = info->descriptor;
+
+      if (se->want_pointer)
+       se->expr = gfc_build_addr_expr (NULL_TREE, desc);
+      else
+       se->expr = desc;
+
+      if (expr->ts.type == BT_CHARACTER)
+       se->string_length = expr->symtree->n.sym->ts.cl->backend_decl;
+    }
   else
     {
-      /* We pass sections without copying to a temporary.  A function may
-         decide to repack the array to speed up access, but we're not
-         bothered about that here.  */
+      /* We pass sections without copying to a temporary.  Make a new
+        descriptor and point it at the section we want.  The loop variable
+        limits will be the limits of the section.
+        A function may decide to repack the array to speed up access, but
+        we're not bothered about that here.  */
       int dim;
       tree parm;
       tree parmtype;
@@ -3458,9 +3580,10 @@ gfc_conv_expr_descriptor (gfc_se * se, gfc_expr * expr, gfc_ss * ss)
       tree to;
       tree base;
 
-      /* Otherwise make a new descriptor and point it at the section we
-         want.  The loop variable limits will be the limits of the section.
-       */
+      /* Set the string_length for a character array.  */
+      if (expr->ts.type == BT_CHARACTER)
+       se->string_length = expr->symtree->n.sym->ts.cl->backend_decl;
+
       desc = info->descriptor;
       assert (secss && secss != gfc_ss_terminator);
       if (se->direct_byref)
@@ -3632,6 +3755,8 @@ gfc_conv_array_parameter (gfc_se * se, gfc_expr * expr, gfc_ss * ss, int g77)
     {
       sym = expr->symtree->n.sym;
       tmp = gfc_get_symbol_decl (sym);
+      if (sym->ts.type == BT_CHARACTER)
+       se->string_length = sym->ts.cl->backend_decl;
       if (!sym->attr.pointer && sym->as->type != AS_ASSUMED_SHAPE 
           && !sym->attr.allocatable)
         {