friend.c revision 52284
150397Sobrien/* Help friends in C++.
252284Sobrien   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
350397Sobrien
450397SobrienThis file is part of GNU CC.
550397Sobrien
650397SobrienGNU CC is free software; you can redistribute it and/or modify
750397Sobrienit under the terms of the GNU General Public License as published by
850397Sobrienthe Free Software Foundation; either version 2, or (at your option)
950397Sobrienany later version.
1050397Sobrien
1150397SobrienGNU CC is distributed in the hope that it will be useful,
1250397Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of
1350397SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1450397SobrienGNU General Public License for more details.
1550397Sobrien
1650397SobrienYou should have received a copy of the GNU General Public License
1750397Sobrienalong with GNU CC; see the file COPYING.  If not, write to
1850397Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330,
1950397SobrienBoston, MA 02111-1307, USA.  */
2050397Sobrien
2150397Sobrien#include "config.h"
2250397Sobrien#include "system.h"
2350397Sobrien#include "tree.h"
2450397Sobrien#include "rtl.h"
2550397Sobrien#include "cp-tree.h"
2650397Sobrien#include "flags.h"
2750397Sobrien#include "output.h"
2850397Sobrien#include "toplev.h"
2950397Sobrien
3050397Sobrien/* Friend data structures are described in cp-tree.h.  */
3150397Sobrien
3252284Sobrien/* Returns non-zero if SUPPLICANT is a friend of TYPE.  */
3352284Sobrien
3450397Sobrienint
3550397Sobrienis_friend (type, supplicant)
3650397Sobrien     tree type, supplicant;
3750397Sobrien{
3850397Sobrien  int declp;
3950397Sobrien  register tree list;
4050397Sobrien  tree context;
4150397Sobrien
4250397Sobrien  if (supplicant == NULL_TREE || type == NULL_TREE)
4350397Sobrien    return 0;
4450397Sobrien
4550397Sobrien  declp = (TREE_CODE_CLASS (TREE_CODE (supplicant)) == 'd');
4650397Sobrien
4750397Sobrien  if (declp)
4850397Sobrien    /* It's a function decl.  */
4950397Sobrien    {
5050397Sobrien      tree list = DECL_FRIENDLIST (TYPE_MAIN_DECL (type));
5150397Sobrien      tree name = DECL_NAME (supplicant);
5250397Sobrien      tree ctype;
5350397Sobrien
5450397Sobrien      if (DECL_FUNCTION_MEMBER_P (supplicant))
5550397Sobrien	ctype = DECL_CLASS_CONTEXT (supplicant);
5650397Sobrien      else
5750397Sobrien	ctype = NULL_TREE;
5850397Sobrien
5950397Sobrien      for (; list ; list = TREE_CHAIN (list))
6050397Sobrien	{
6152284Sobrien	  if (name == FRIEND_NAME (list))
6250397Sobrien	    {
6352284Sobrien	      tree friends = FRIEND_DECLS (list);
6450397Sobrien	      for (; friends ; friends = TREE_CHAIN (friends))
6550397Sobrien		{
6652284Sobrien		  if (same_type_p (ctype, TREE_PURPOSE (friends)))
6750397Sobrien		    return 1;
6850397Sobrien
6950397Sobrien		  if (TREE_VALUE (friends) == NULL_TREE)
7050397Sobrien		    continue;
7150397Sobrien
7252284Sobrien		  if (supplicant == TREE_VALUE (friends))
7352284Sobrien		    return 1;
7450397Sobrien
7552284Sobrien		  /* With -fguiding-decls we are more lenient about
7652284Sobrien		     friendship.  This is bogus in general since two
7752284Sobrien		     specializations of a template with non-type
7852284Sobrien		     template parameters may have the same type, but
7952284Sobrien		     be different.
8050397Sobrien
8152284Sobrien		     Temporarily, we are also more lenient to deal
8252284Sobrien		     with nested friend functions, for which there can
8352284Sobrien		     be more than one FUNCTION_DECL, despite being the
8452284Sobrien		     same function.  When that's fixed, the
8552284Sobrien		     FUNCTION_MEMBER_P bit can go.  */
8652284Sobrien		  if ((flag_guiding_decls
8752284Sobrien		       || DECL_FUNCTION_MEMBER_P (supplicant))
8852284Sobrien		      && same_type_p (TREE_TYPE (supplicant),
8952284Sobrien				      TREE_TYPE (TREE_VALUE (friends))))
9050397Sobrien		    return 1;
9152284Sobrien
9252284Sobrien		  if (TREE_CODE (TREE_VALUE (friends)) == TEMPLATE_DECL
9352284Sobrien		      && is_specialization_of (supplicant,
9452284Sobrien					       TREE_VALUE (friends)))
9552284Sobrien		    return 1;
9650397Sobrien		}
9750397Sobrien	      break;
9850397Sobrien	    }
9950397Sobrien	}
10050397Sobrien    }
10150397Sobrien  else
10250397Sobrien    /* It's a type.  */
10350397Sobrien    {
10450397Sobrien      if (type == supplicant)
10550397Sobrien	return 1;
10650397Sobrien
10750397Sobrien      list = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (TYPE_MAIN_DECL (type)));
10850397Sobrien      for (; list ; list = TREE_CHAIN (list))
10950397Sobrien	{
11050397Sobrien	  tree t = TREE_VALUE (list);
11150397Sobrien
11250397Sobrien	  if (TREE_CODE (t) == TEMPLATE_DECL ?
11350397Sobrien	      is_specialization_of (TYPE_MAIN_DECL (supplicant), t) :
11452284Sobrien	      same_type_p (supplicant, t))
11550397Sobrien	    return 1;
11650397Sobrien	}
11750397Sobrien    }
11850397Sobrien
11950397Sobrien  if (declp && DECL_FUNCTION_MEMBER_P (supplicant))
12050397Sobrien    context = DECL_CLASS_CONTEXT (supplicant);
12150397Sobrien  else if (! declp)
12250397Sobrien    /* Local classes have the same access as the enclosing function.  */
12350397Sobrien    context = hack_decl_function_context (TYPE_MAIN_DECL (supplicant));
12450397Sobrien  else
12550397Sobrien    context = NULL_TREE;
12650397Sobrien
12750397Sobrien  /* A namespace is not friend to anybody. */
12850397Sobrien  if (context && TREE_CODE (context) == NAMESPACE_DECL)
12950397Sobrien    context = NULL_TREE;
13050397Sobrien
13150397Sobrien  if (context)
13250397Sobrien    return is_friend (type, context);
13350397Sobrien
13450397Sobrien  return 0;
13550397Sobrien}
13650397Sobrien
13750397Sobrien/* Add a new friend to the friends of the aggregate type TYPE.
13850397Sobrien   DECL is the FUNCTION_DECL of the friend being added.  */
13950397Sobrien
14052284Sobrienvoid
14150397Sobrienadd_friend (type, decl)
14250397Sobrien     tree type, decl;
14350397Sobrien{
14452284Sobrien  tree typedecl;
14552284Sobrien  tree list;
14652284Sobrien  tree name;
14750397Sobrien
14852284Sobrien  if (decl == error_mark_node)
14952284Sobrien    return;
15052284Sobrien
15152284Sobrien  typedecl = TYPE_MAIN_DECL (type);
15252284Sobrien  list = DECL_FRIENDLIST (typedecl);
15352284Sobrien  name = DECL_NAME (decl);
15452284Sobrien  type = TREE_TYPE (typedecl);
15552284Sobrien
15650397Sobrien  while (list)
15750397Sobrien    {
15852284Sobrien      if (name == FRIEND_NAME (list))
15950397Sobrien	{
16052284Sobrien	  tree friends = FRIEND_DECLS (list);
16150397Sobrien	  for (; friends ; friends = TREE_CHAIN (friends))
16250397Sobrien	    {
16350397Sobrien	      if (decl == TREE_VALUE (friends))
16450397Sobrien		{
16550397Sobrien		  cp_warning ("`%D' is already a friend of class `%T'",
16650397Sobrien			      decl, type);
16750397Sobrien		  cp_warning_at ("previous friend declaration of `%D'",
16850397Sobrien				 TREE_VALUE (friends));
16950397Sobrien		  return;
17050397Sobrien		}
17150397Sobrien	    }
17250397Sobrien	  TREE_VALUE (list) = tree_cons (error_mark_node, decl,
17350397Sobrien					 TREE_VALUE (list));
17450397Sobrien	  return;
17550397Sobrien	}
17650397Sobrien      list = TREE_CHAIN (list);
17750397Sobrien    }
17852284Sobrien
17950397Sobrien  DECL_FRIENDLIST (typedecl)
18050397Sobrien    = tree_cons (DECL_NAME (decl), build_tree_list (error_mark_node, decl),
18150397Sobrien		 DECL_FRIENDLIST (typedecl));
18252284Sobrien  if (!uses_template_parms (type))
18352284Sobrien    DECL_BEFRIENDING_CLASSES (decl)
18452284Sobrien      = tree_cons (NULL_TREE, type,
18552284Sobrien		   DECL_BEFRIENDING_CLASSES (decl));
18650397Sobrien}
18750397Sobrien
18850397Sobrien/* Declare that every member function NAME in FRIEND_TYPE
18950397Sobrien   (which may be NULL_TREE) is a friend of type TYPE.  */
19050397Sobrien
19152284Sobrienvoid
19250397Sobrienadd_friends (type, name, friend_type)
19350397Sobrien     tree type, name, friend_type;
19450397Sobrien{
19550397Sobrien  tree typedecl = TYPE_MAIN_DECL (type);
19650397Sobrien  tree list = DECL_FRIENDLIST (typedecl);
19750397Sobrien
19850397Sobrien  while (list)
19950397Sobrien    {
20052284Sobrien      if (name == FRIEND_NAME (list))
20150397Sobrien	{
20252284Sobrien	  tree friends = FRIEND_DECLS (list);
20350397Sobrien	  while (friends && TREE_PURPOSE (friends) != friend_type)
20450397Sobrien	    friends = TREE_CHAIN (friends);
20550397Sobrien	  if (friends)
20650397Sobrien	    {
20750397Sobrien	      if (friend_type)
20850397Sobrien		warning ("method `%s::%s' is already a friend of class",
20950397Sobrien			 TYPE_NAME_STRING (friend_type),
21050397Sobrien			 IDENTIFIER_POINTER (name));
21150397Sobrien	      else
21250397Sobrien		warning ("function `%s' is already a friend of class `%s'",
21350397Sobrien			 IDENTIFIER_POINTER (name),
21450397Sobrien			 IDENTIFIER_POINTER (DECL_NAME (typedecl)));
21550397Sobrien	    }
21650397Sobrien	  else
21750397Sobrien	    TREE_VALUE (list) = tree_cons (friend_type, NULL_TREE,
21850397Sobrien					   TREE_VALUE (list));
21950397Sobrien	  return;
22050397Sobrien	}
22150397Sobrien      list = TREE_CHAIN (list);
22250397Sobrien    }
22350397Sobrien  DECL_FRIENDLIST (typedecl)
22450397Sobrien    = tree_cons (name,
22550397Sobrien		 build_tree_list (friend_type, NULL_TREE),
22650397Sobrien		 DECL_FRIENDLIST (typedecl));
22750397Sobrien}
22850397Sobrien
22950397Sobrien/* Make FRIEND_TYPE a friend class to TYPE.  If FRIEND_TYPE has already
23050397Sobrien   been defined, we make all of its member functions friends of
23150397Sobrien   TYPE.  If not, we make it a pending friend, which can later be added
23250397Sobrien   when its definition is seen.  If a type is defined, then its TYPE_DECL's
23350397Sobrien   DECL_UNDEFINED_FRIENDS contains a (possibly empty) list of friend
23450397Sobrien   classes that are not defined.  If a type has not yet been defined,
23550397Sobrien   then the DECL_WAITING_FRIENDS contains a list of types
23650397Sobrien   waiting to make it their friend.  Note that these two can both
23750397Sobrien   be in use at the same time!  */
23850397Sobrien
23950397Sobrienvoid
24050397Sobrienmake_friend_class (type, friend_type)
24150397Sobrien     tree type, friend_type;
24250397Sobrien{
24350397Sobrien  tree classes;
24450397Sobrien  int is_template_friend;
24550397Sobrien
24650397Sobrien  if (IS_SIGNATURE (type))
24750397Sobrien    {
24850397Sobrien      error ("`friend' declaration in signature definition");
24950397Sobrien      return;
25050397Sobrien    }
25152284Sobrien  if (IS_SIGNATURE (friend_type) || ! IS_AGGR_TYPE (friend_type))
25250397Sobrien    {
25352284Sobrien      cp_error ("invalid type `%T' declared `friend'", friend_type);
25450397Sobrien      return;
25550397Sobrien    }
25652284Sobrien
25752284Sobrien  if (CLASS_TYPE_P (friend_type)
25852284Sobrien      && CLASSTYPE_TEMPLATE_SPECIALIZATION (friend_type)
25952284Sobrien      && uses_template_parms (friend_type))
26052284Sobrien    {
26152284Sobrien      /* [temp.friend]
26252284Sobrien
26352284Sobrien	 Friend declarations shall not declare partial
26452284Sobrien	 specializations.  */
26552284Sobrien      cp_error ("partial specialization `%T' declared `friend'",
26652284Sobrien		friend_type);
26752284Sobrien      return;
26852284Sobrien    }
26952284Sobrien
27050397Sobrien  if (processing_template_decl > template_class_depth (type))
27150397Sobrien    /* If the TYPE is a template then it makes sense for it to be
27250397Sobrien       friends with itself; this means that each instantiation is
27350397Sobrien       friends with all other instantiations.  */
27450397Sobrien    is_template_friend = 1;
27552284Sobrien  else if (same_type_p (type, friend_type))
27650397Sobrien    {
27750397Sobrien      pedwarn ("class `%s' is implicitly friends with itself",
27850397Sobrien	       TYPE_NAME_STRING (type));
27950397Sobrien      return;
28050397Sobrien    }
28150397Sobrien  else
28250397Sobrien    is_template_friend = 0;
28350397Sobrien
28450397Sobrien  GNU_xref_hier (type, friend_type, 0, 0, 1);
28550397Sobrien
28650397Sobrien  if (is_template_friend)
28750397Sobrien    friend_type = CLASSTYPE_TI_TEMPLATE (friend_type);
28850397Sobrien
28950397Sobrien  classes = CLASSTYPE_FRIEND_CLASSES (type);
29050397Sobrien  while (classes
29150397Sobrien	 /* Stop if we find the same type on the list.  */
29250397Sobrien	 && !(TREE_CODE (TREE_VALUE (classes)) == TEMPLATE_DECL ?
29350397Sobrien	      friend_type == TREE_VALUE (classes) :
29452284Sobrien	      same_type_p (TREE_VALUE (classes), friend_type)))
29550397Sobrien    classes = TREE_CHAIN (classes);
29650397Sobrien  if (classes)
29750397Sobrien    cp_warning ("`%T' is already a friend of `%T'",
29850397Sobrien		TREE_VALUE (classes), type);
29950397Sobrien  else
30050397Sobrien    {
30150397Sobrien      CLASSTYPE_FRIEND_CLASSES (type)
30250397Sobrien	= tree_cons (NULL_TREE, friend_type, CLASSTYPE_FRIEND_CLASSES (type));
30352284Sobrien      if (is_template_friend)
30452284Sobrien	friend_type = TREE_TYPE (friend_type);
30552284Sobrien      if (!uses_template_parms (type))
30652284Sobrien	CLASSTYPE_BEFRIENDING_CLASSES (friend_type)
30752284Sobrien	  = tree_cons (NULL_TREE, type,
30852284Sobrien		       CLASSTYPE_BEFRIENDING_CLASSES (friend_type));
30950397Sobrien    }
31050397Sobrien}
31150397Sobrien
31250397Sobrien/* Main friend processor.  This is large, and for modularity purposes,
31350397Sobrien   has been removed from grokdeclarator.  It returns `void_type_node'
31450397Sobrien   to indicate that something happened, though a FIELD_DECL is
31550397Sobrien   not returned.
31650397Sobrien
31750397Sobrien   CTYPE is the class this friend belongs to.
31850397Sobrien
31950397Sobrien   DECLARATOR is the name of the friend.
32050397Sobrien
32150397Sobrien   DECL is the FUNCTION_DECL that the friend is.
32250397Sobrien
32350397Sobrien   In case we are parsing a friend which is part of an inline
32450397Sobrien   definition, we will need to store PARM_DECL chain that comes
32550397Sobrien   with it into the DECL_ARGUMENTS slot of the FUNCTION_DECL.
32650397Sobrien
32750397Sobrien   FLAGS is just used for `grokclassfn'.
32850397Sobrien
32950397Sobrien   QUALS say what special qualifies should apply to the object
33050397Sobrien   pointed to by `this'.  */
33150397Sobrien
33250397Sobrientree
33352284Sobriendo_friend (ctype, declarator, decl, parmdecls, attrlist,
33452284Sobrien	   flags, quals, funcdef_flag)
33552284Sobrien     tree ctype, declarator, decl, parmdecls, attrlist;
33650397Sobrien     enum overload_flags flags;
33750397Sobrien     tree quals;
33850397Sobrien     int funcdef_flag;
33950397Sobrien{
34050397Sobrien  int is_friend_template = 0;
34152284Sobrien  tree prefix_attributes, attributes;
34250397Sobrien
34350397Sobrien  /* Every decl that gets here is a friend of something.  */
34450397Sobrien  DECL_FRIEND_P (decl) = 1;
34550397Sobrien
34650397Sobrien  if (TREE_CODE (declarator) == TEMPLATE_ID_EXPR)
34750397Sobrien    {
34850397Sobrien      declarator = TREE_OPERAND (declarator, 0);
34950397Sobrien      if (TREE_CODE (declarator) == LOOKUP_EXPR)
35050397Sobrien	declarator = TREE_OPERAND (declarator, 0);
35150397Sobrien      if (is_overloaded_fn (declarator))
35250397Sobrien	declarator = DECL_NAME (get_first_fn (declarator));
35350397Sobrien    }
35450397Sobrien
35552284Sobrien  if (TREE_CODE (decl) != FUNCTION_DECL)
35652284Sobrien    my_friendly_abort (990513);
35750397Sobrien
35852284Sobrien  is_friend_template = PROCESSING_REAL_TEMPLATE_DECL_P ();
35952284Sobrien
36050397Sobrien  if (ctype)
36150397Sobrien    {
36250397Sobrien      tree cname = TYPE_NAME (ctype);
36350397Sobrien      if (TREE_CODE (cname) == TYPE_DECL)
36450397Sobrien	cname = DECL_NAME (cname);
36550397Sobrien
36650397Sobrien      /* A method friend.  */
36752284Sobrien      if (flags == NO_SPECIAL && ctype && declarator == cname)
36852284Sobrien	DECL_CONSTRUCTOR_P (decl) = 1;
36950397Sobrien
37052284Sobrien      /* This will set up DECL_ARGUMENTS for us.  */
37152284Sobrien      grokclassfn (ctype, decl, flags, quals);
37250397Sobrien
37352284Sobrien      if (is_friend_template)
37452284Sobrien	decl = DECL_TI_TEMPLATE (push_template_decl (decl));
37552284Sobrien      else if (template_class_depth (current_class_type))
37652284Sobrien	decl = push_template_decl_real (decl, /*is_friend=*/1);
37750397Sobrien
37852284Sobrien      /* We can't do lookup in a type that involves template
37952284Sobrien	 parameters.  Instead, we rely on tsubst_friend_function
38052284Sobrien	 to check the validity of the declaration later.  */
38152284Sobrien      if (uses_template_parms (ctype))
38252284Sobrien	add_friend (current_class_type, decl);
38352284Sobrien      /* A nested class may declare a member of an enclosing class
38452284Sobrien	 to be a friend, so we do lookup here even if CTYPE is in
38552284Sobrien	 the process of being defined.  */
38652284Sobrien      else if (TYPE_SIZE (ctype) != 0 || TYPE_BEING_DEFINED (ctype))
38752284Sobrien	{
38852284Sobrien	  decl = check_classfn (ctype, decl);
38950397Sobrien
39052284Sobrien	  if (decl)
39152284Sobrien	    add_friend (current_class_type, decl);
39250397Sobrien	}
39350397Sobrien      else
39452284Sobrien	cp_error ("member `%D' declared as friend before type `%T' defined",
39552284Sobrien		  decl, ctype);
39650397Sobrien    }
39750397Sobrien  /* A global friend.
39850397Sobrien     @@ or possibly a friend from a base class ?!?  */
39950397Sobrien  else if (TREE_CODE (decl) == FUNCTION_DECL)
40050397Sobrien    {
40150397Sobrien      /* Friends must all go through the overload machinery,
40250397Sobrien	 even though they may not technically be overloaded.
40350397Sobrien
40450397Sobrien	 Note that because classes all wind up being top-level
40550397Sobrien	 in their scope, their friend wind up in top-level scope as well.  */
40650397Sobrien      DECL_ARGUMENTS (decl) = parmdecls;
40750397Sobrien      if (funcdef_flag)
40850397Sobrien	DECL_CLASS_CONTEXT (decl) = current_class_type;
40950397Sobrien
41050397Sobrien      if (! DECL_USE_TEMPLATE (decl))
41150397Sobrien	{
41250397Sobrien	  /* We can call pushdecl here, because the TREE_CHAIN of this
41352284Sobrien	     FUNCTION_DECL is not needed for other purposes.  Don't do
41452284Sobrien	     this for a template instantiation.  However, we don't
41552284Sobrien	     call pushdecl() for a friend function of a template
41652284Sobrien	     class, since in general, such a declaration depends on
41752284Sobrien	     template parameters.  Instead, we call pushdecl when the
41852284Sobrien	     class is instantiated.  */
41952284Sobrien	  if (!is_friend_template
42052284Sobrien	      && template_class_depth (current_class_type) == 0)
42152284Sobrien	    decl = pushdecl (decl);
42250397Sobrien	  else
42352284Sobrien	    decl = push_template_decl_real (decl, /*is_friend=*/1);
42450397Sobrien
42552284Sobrien	  if (warn_nontemplate_friend
42652284Sobrien	      && ! funcdef_flag && ! flag_guiding_decls && ! is_friend_template
42750397Sobrien	      && current_template_parms && uses_template_parms (decl))
42850397Sobrien	    {
42950397Sobrien	      static int explained;
43050397Sobrien	      cp_warning ("friend declaration `%#D'", decl);
43150397Sobrien	      warning ("  declares a non-template function");
43250397Sobrien	      if (! explained)
43350397Sobrien		{
43450397Sobrien		  warning ("  (if this is not what you intended, make sure");
43550397Sobrien		  warning ("  the function template has already been declared,");
43650397Sobrien		  warning ("  and add <> after the function name here)");
43752284Sobrien 		  warning ("  -Wno-non-template-friend disables this warning.");
43850397Sobrien		  explained = 1;
43950397Sobrien		}
44050397Sobrien	    }
44150397Sobrien	}
44250397Sobrien
44350397Sobrien      make_decl_rtl (decl, NULL_PTR, 1);
44450397Sobrien      add_friend (current_class_type,
44550397Sobrien		  is_friend_template ? DECL_TI_TEMPLATE (decl) : decl);
44650397Sobrien      DECL_FRIEND_P (decl) = 1;
44750397Sobrien    }
44852284Sobrien
44952284Sobrien  /* Unfortunately, we have to handle attributes here.  Normally we would
45052284Sobrien     handle them in start_decl_1, but since this is a friend decl start_decl_1
45152284Sobrien     never gets to see it.  */
45252284Sobrien
45352284Sobrien  if (attrlist)
45452284Sobrien    {
45552284Sobrien      attributes = TREE_PURPOSE (attrlist);
45652284Sobrien      prefix_attributes = TREE_VALUE (attrlist);
45752284Sobrien    }
45850397Sobrien  else
45950397Sobrien    {
46052284Sobrien      attributes = NULL_TREE;
46152284Sobrien      prefix_attributes = NULL_TREE;
46252284Sobrien    }
46350397Sobrien
46452284Sobrien#ifdef SET_DEFAULT_DECL_ATTRIBUTES
46552284Sobrien  SET_DEFAULT_DECL_ATTRIBUTES (decl, attributes);
46652284Sobrien#endif
46752284Sobrien
46852284Sobrien  /* Set attributes here so if duplicate decl, will have proper attributes.  */
46952284Sobrien  cplus_decl_attributes (decl, attributes, prefix_attributes);
47050397Sobrien
47150397Sobrien  return decl;
47250397Sobrien}
473