This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch][gcc-4_1-branch][PR c++/29704]


I've discovered that this patch, which is already on the trunk and on gcc-4_2-branch, fixes PR c++/29704 on gcc-4_1-branch where the problem was reported.

Dave

2006-08-20  Mark Mitchell  <mark@codesourcery.com>

	PR c++/28341
	* tree.c (cast_valid_in_integral_constant_expression_p): New
	function.
	* cp-tree.h (tsubst_copy_and_build): Adjust prototype.
	* pt.c (tsubst_expr): Add integral_constant_expression_p
	parameter.
	(fold_non_dependent_expr): Adjust callers of
	tsubst_{expr,copy_and_build}.
	(tsubst_friend_function): Likewise.
	(tsubst_template_arg): Likewise.
	(tsubst_default_argument): Likewise.
	(tsubst_decl): Likewise.
	(tsubst): Likewise.
	(tsubst_omp_clasuses): Likewise.
	(regenerate_decl_fromp_template): Likewise.
	(instantiate_decl): Likewise.
	(tsubst_initializer_list): Likewise.
	(tsubst_enum): Likewise.
	(tsubst_expr): Use RECUR throughout.
	(tsubst_copy_and_build): Change definition of RECUR.  Do not allow
	invalid casts in integral constant expressions.
Index: gcc/testsuite/g++.dg/template/nontype13.C
===================================================================
--- gcc/testsuite/g++.dg/template/nontype13.C	(revision 125313)
+++ gcc/testsuite/g++.dg/template/nontype13.C	(working copy)
@@ -11,7 +11,7 @@
   template<bool B>
   void tester()
   {
-    bar<evil>()(); // { dg-error "argument" }
+    bar<evil>()(); // { dg-error "constant" }
   }
   template<bool B>
   struct bar
Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	(revision 125313)
+++ gcc/cp/tree.c	(working copy)
@@ -2338,6 +2338,17 @@
   return fold (expr);
 }
 
+/* Returns true if a cast to TYPE may appear in an integral constant
+   expression.  */
+
+bool
+cast_valid_in_integral_constant_expression_p (tree type)
+{
+  return (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
+	  || dependent_type_p (type)
+	  || type == error_mark_node);
+}
+
 
 #if defined ENABLE_TREE_CHECKING && (GCC_VERSION >= 2007)
 /* Complain that some language-specific thing hanging off a tree
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 125313)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -4105,7 +4105,7 @@
 extern void instantiate_pending_templates	(int);
 extern tree tsubst_default_argument		(tree, tree, tree);
 extern tree tsubst_copy_and_build		(tree, tree, tsubst_flags_t,
-						 tree, bool);
+						 tree, bool, bool);
 extern tree most_general_template		(tree);
 extern tree get_mostly_instantiated_function_type (tree);
 extern int problematic_instantiation_changed	(void);
@@ -4362,6 +4362,7 @@
 extern int cp_auto_var_in_fn_p			(tree,tree);
 extern tree fold_if_not_in_template		(tree);
 extern tree rvalue                              (tree);
+extern bool cast_valid_in_integral_constant_expression_p (tree);
    
 /* in typeck.c */
 extern int string_conv_p			(tree, tree, int);
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 125313)
+++ gcc/cp/pt.c	(working copy)
@@ -159,7 +159,7 @@
 static int eq_local_specializations (const void *, const void *);
 static bool dependent_type_p_r (tree);
 static tree tsubst (tree, tree, tsubst_flags_t, tree);
-static tree tsubst_expr	(tree, tree, tsubst_flags_t, tree);
+static tree tsubst_expr	(tree, tree, tsubst_flags_t, tree, bool);
 static tree tsubst_copy	(tree, tree, tsubst_flags_t, tree);
 
 /* Make the current scope suitable for access checking when we are
@@ -3407,7 +3407,8 @@
 				    /*args=*/NULL_TREE,
 				    tf_error,
 				    /*in_decl=*/NULL_TREE,
-				    /*function_p=*/false);
+				    /*function_p=*/false,
+				    /*integral_constant_expression_p=*/true);
       processing_template_decl = saved_processing_template_decl;
     }
   return expr;
@@ -5215,7 +5216,8 @@
 	 current cless with same name.  */
       push_nested_namespace (ns);
       fns = tsubst_expr (DECL_TI_TEMPLATE (decl), args,
-			 tf_error | tf_warning, NULL_TREE);
+			 tf_error | tf_warning, NULL_TREE,
+			 /*integral_constant_expression_p=*/false);
       pop_nested_namespace (ns);
       arglist = tsubst (DECL_TI_ARGS (decl), args,
 			tf_error | tf_warning, NULL_TREE);
@@ -6025,7 +6027,8 @@
     r = tsubst (t, args, complain, in_decl);
   else
     {
-      r = tsubst_expr (t, args, complain, in_decl);
+      r = tsubst_expr (t, args, complain, in_decl,
+		       /*integral_constant_expression_p=*/true);
       r = fold_non_dependent_expr (r);
     }
   return r;
@@ -6239,7 +6242,8 @@
       stack.  */
   ++function_depth;
   arg = tsubst_expr (arg, DECL_TI_ARGS (fn),
-		     tf_error | tf_warning, NULL_TREE);
+		     tf_error | tf_warning, NULL_TREE,
+		     /*integral_constant_expression_p=*/false);
   --function_depth;
   pop_deferring_access_checks();
 
@@ -6693,7 +6697,8 @@
 	/* We don't have to set DECL_CONTEXT here; it is set by
 	   finish_member_declaration.  */
 	DECL_INITIAL (r) = tsubst_expr (DECL_INITIAL (t), args,
-					complain, in_decl);
+					complain, in_decl,
+					/*integral_constant_expression_p=*/true);
 	TREE_CHAIN (r) = NULL_TREE;
 	if (VOID_TYPE_P (type))
 	  error ("instantiation of %q+D as type %qT", r, type);
@@ -6842,7 +6847,8 @@
 	    if (DECL_HAS_VALUE_EXPR_P (t))
 	      {
 		tree ve = DECL_VALUE_EXPR (t);
-		ve = tsubst_expr (ve, args, complain, in_decl);
+		ve = tsubst_expr (ve, args, complain, in_decl,
+				  /*constant_expression_p=*/false);
 	        SET_DECL_VALUE_EXPR (r, ve);
 	      }
 	  }
@@ -7094,7 +7100,8 @@
 
   /* Figure out the default argument as well.  Note that we use
      tsubst_expr since the default argument is really an expression.  */
-  defarg = tsubst_expr (TREE_PURPOSE (parms), args, complain, in_decl);
+  defarg = tsubst_expr (TREE_PURPOSE (parms), args, complain, in_decl,
+			/*integral_constant_expression_p=*/false);
 
   /* Chain this parameter on to the front of those we have already
      processed.  We don't use hash_tree_cons because that function
@@ -7677,7 +7684,8 @@
     case ARRAY_REF:
       {
 	tree e1 = tsubst (TREE_OPERAND (t, 0), args, complain, in_decl);
-	tree e2 = tsubst_expr (TREE_OPERAND (t, 1), args, complain, in_decl);
+	tree e2 = tsubst_expr (TREE_OPERAND (t, 1), args, complain, in_decl,
+			       /*integral_constant_expression_p=*/false);
 	if (e1 == error_mark_node || e2 == error_mark_node)
 	  return error_mark_node;
 
@@ -7699,8 +7707,10 @@
       {
 	tree type;
 
-	type = finish_typeof (tsubst_expr (TYPEOF_TYPE_EXPR (t), args,
-					   complain, in_decl));
+	type = finish_typeof (tsubst_expr
+			      (TYPEOF_TYPE_EXPR (t), args,
+			       complain, in_decl,
+			       /*integral_constant_expression_p=*/false));
 	return cp_build_qualified_type_real (type,
 					     cp_type_quals (t)
 					     | cp_type_quals (type),
@@ -8287,7 +8297,8 @@
 
   if (TREE_CODE (t) != TREE_LIST)
     return tsubst_copy_and_build (t, args, complain, in_decl,
-				  /*function_p=*/false);
+				  /*function_p=*/false,
+				  /*integral_constant_expression_p=*/false);
 
   if (t == void_list_node)
     return t;
@@ -8309,8 +8320,12 @@
    processing.  */
 
 static tree
-tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
+tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
+	     bool integral_constant_expression_p)
 {
+#define RECUR(NODE)				\
+  tsubst_expr ((NODE), args, complain, in_decl,	\
+	       integral_constant_expression_p)
   tree stmt, tmp;
 
   if (t == NULL_TREE || t == error_mark_node)
@@ -8327,7 +8342,7 @@
       {
 	tree_stmt_iterator i;
 	for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
-	  tsubst_expr (tsi_stmt (i), args, complain, in_decl);
+	  RECUR (tsi_stmt (i));
 	break;
       }
 
@@ -8337,12 +8352,11 @@
       break;
 
     case RETURN_EXPR:
-      finish_return_stmt (tsubst_expr (TREE_OPERAND (t, 0),
-				       args, complain, in_decl));
+      finish_return_stmt (RECUR (TREE_OPERAND (t, 0)));
       break;
 
     case EXPR_STMT:
-      tmp = tsubst_expr (EXPR_STMT_EXPR (t), args, complain, in_decl);
+      tmp = RECUR (EXPR_STMT_EXPR (t));
       if (EXPR_STMT_STMT_EXPR_RESULT (t))
 	finish_stmt_expr_expr (tmp, cur_stmt_expr);
       else
@@ -8350,8 +8364,7 @@
       break;
 
     case USING_STMT:
-      do_using_directive (tsubst_expr (USING_STMT_NAMESPACE (t),
-				       args, complain, in_decl));
+      do_using_directive (RECUR (USING_STMT_NAMESPACE (t)));
       break;
 
     case DECL_EXPR:
@@ -8368,7 +8381,7 @@
 	    tree name = DECL_NAME (decl);
 	    tree decl;
 
-	    scope = tsubst_expr (scope, args, complain, in_decl);
+	    scope = RECUR (scope);
 	    decl = lookup_qualified_name (scope, name,
 					  /*is_type_p=*/false,
 					  /*complain=*/false);
@@ -8409,7 +8422,7 @@
 			init = cp_fname_init (name, &TREE_TYPE (decl));
 		      }
 		    else
-		      init = tsubst_expr (init, args, complain, in_decl);
+		      init = RECUR (init);
 		    finish_decl (decl, init, NULL_TREE);
 		  }
 	      }
@@ -8422,43 +8435,43 @@
 
     case FOR_STMT:
       stmt = begin_for_stmt ();
-      tsubst_expr (FOR_INIT_STMT (t), args, complain, in_decl);
+      RECUR (FOR_INIT_STMT (t));
       finish_for_init_stmt (stmt);
-      tmp = tsubst_expr (FOR_COND (t), args, complain, in_decl);
+      tmp = RECUR (FOR_COND (t));
       finish_for_cond (tmp, stmt);
-      tmp = tsubst_expr (FOR_EXPR (t), args, complain, in_decl);
+      tmp = RECUR (FOR_EXPR (t));
       finish_for_expr (tmp, stmt);
-      tsubst_expr (FOR_BODY (t), args, complain, in_decl);
+      RECUR (FOR_BODY (t));
       finish_for_stmt (stmt);
       break;
 
     case WHILE_STMT:
       stmt = begin_while_stmt ();
-      tmp = tsubst_expr (WHILE_COND (t), args, complain, in_decl);
+      tmp = RECUR (WHILE_COND (t));
       finish_while_stmt_cond (tmp, stmt);
-      tsubst_expr (WHILE_BODY (t), args, complain, in_decl);
+      RECUR (WHILE_BODY (t));
       finish_while_stmt (stmt);
       break;
 
     case DO_STMT:
       stmt = begin_do_stmt ();
-      tsubst_expr (DO_BODY (t), args, complain, in_decl);
+      RECUR (DO_BODY (t));
       finish_do_body (stmt);
-      tmp = tsubst_expr (DO_COND (t), args, complain, in_decl);
+      tmp = RECUR (DO_COND (t));
       finish_do_stmt (tmp, stmt);
       break;
 
     case IF_STMT:
       stmt = begin_if_stmt ();
-      tmp = tsubst_expr (IF_COND (t), args, complain, in_decl);
+      tmp = RECUR (IF_COND (t));
       finish_if_stmt_cond (tmp, stmt);
-      tsubst_expr (THEN_CLAUSE (t), args, complain, in_decl);
+      RECUR (THEN_CLAUSE (t));
       finish_then_clause (stmt);
 
       if (ELSE_CLAUSE (t))
 	{
 	  begin_else_clause (stmt);
-	  tsubst_expr (ELSE_CLAUSE (t), args, complain, in_decl);
+	  RECUR (ELSE_CLAUSE (t));
 	  finish_else_clause (stmt);
 	}
 
@@ -8472,7 +8485,7 @@
 	stmt = begin_compound_stmt (BIND_EXPR_TRY_BLOCK (t)
 				    ? BCS_TRY_BLOCK : 0);
 
-      tsubst_expr (BIND_EXPR_BODY (t), args, complain, in_decl);
+      RECUR (BIND_EXPR_BODY (t));
 
       if (BIND_EXPR_BODY_BLOCK (t))
 	finish_function_body (stmt);
@@ -8490,16 +8503,15 @@
 
     case SWITCH_STMT:
       stmt = begin_switch_stmt ();
-      tmp = tsubst_expr (SWITCH_STMT_COND (t), args, complain, in_decl);
+      tmp = RECUR (SWITCH_STMT_COND (t));
       finish_switch_cond (tmp, stmt);
-      tsubst_expr (SWITCH_STMT_BODY (t), args, complain, in_decl);
+      RECUR (SWITCH_STMT_BODY (t));
       finish_switch_stmt (stmt);
       break;
 
     case CASE_LABEL_EXPR:
-      finish_case_label (tsubst_expr (CASE_LOW (t), args, complain, in_decl),
-			 tsubst_expr (CASE_HIGH (t), args, complain,
-				      in_decl));
+      finish_case_label (RECUR (CASE_LOW (t)),
+			 RECUR (CASE_HIGH (t)));
       break;
 
     case LABEL_EXPR:
@@ -8512,7 +8524,7 @@
 	/* Computed goto's must be tsubst'd into.  On the other hand,
 	   non-computed gotos must not be; the identifier in question
 	   will have no binding.  */
-	tmp = tsubst_expr (tmp, args, complain, in_decl);
+	tmp = RECUR (tmp);
       else
 	tmp = DECL_NAME (tmp);
       finish_goto_stmt (tmp);
@@ -8521,7 +8533,7 @@
     case ASM_EXPR:
       tmp = finish_asm_stmt
 	(ASM_VOLATILE_P (t),
-	 tsubst_expr (ASM_STRING (t), args, complain, in_decl),
+	 RECUR (ASM_STRING (t)),
 	 tsubst_copy_asm_operands (ASM_OUTPUTS (t), args, complain, in_decl),
 	 tsubst_copy_asm_operands (ASM_INPUTS (t), args, complain, in_decl),
 	 tsubst_copy_asm_operands (ASM_CLOBBERS (t), args, complain, in_decl));
@@ -8537,10 +8549,9 @@
       if (CLEANUP_P (t))
 	{
 	  stmt = begin_try_block ();
-	  tsubst_expr (TRY_STMTS (t), args, complain, in_decl);
+	  RECUR (TRY_STMTS (t));
 	  finish_cleanup_try_block (stmt);
-	  finish_cleanup (tsubst_expr (TRY_HANDLERS (t), args,
-				       complain, in_decl),
+	  finish_cleanup (RECUR (TRY_HANDLERS (t)),
 			  stmt);
 	}
       else
@@ -8552,14 +8563,14 @@
 	  else
 	    stmt = begin_try_block ();
 
-	  tsubst_expr (TRY_STMTS (t), args, complain, in_decl);
+	  RECUR (TRY_STMTS (t));
 
 	  if (FN_TRY_BLOCK_P (t))
 	    finish_function_try_block (stmt);
 	  else
 	    finish_try_block (stmt);
 
-	  tsubst_expr (TRY_HANDLERS (t), args, complain, in_decl);
+	  RECUR (TRY_HANDLERS (t));
 	  if (FN_TRY_BLOCK_P (t))
 	    finish_function_handler_sequence (stmt, compound_stmt);
 	  else
@@ -8584,7 +8595,7 @@
 	else
 	  decl = NULL_TREE;
 	finish_handler_parms (decl, stmt);
-	tsubst_expr (HANDLER_BODY (t), args, complain, in_decl);
+	RECUR (HANDLER_BODY (t));
 	finish_handler (stmt);
       }
       break;
@@ -8597,10 +8608,12 @@
       gcc_assert (!STATEMENT_CODE_P (TREE_CODE (t)));
 
       return tsubst_copy_and_build (t, args, complain, in_decl,
-				    /*function_p=*/false);
+				    /*function_p=*/false,
+				    integral_constant_expression_p);
     }
 
   return NULL_TREE;
+#undef RECUR
 }
 
 /* T is a postfix-expression that is not being used in a function
@@ -8616,7 +8629,8 @@
 			     /*done=*/false, /*address_p=*/false);
   else
     t = tsubst_copy_and_build (t, args, complain, in_decl,
-			       /*function_p=*/false);
+			       /*function_p=*/false,
+			       /*integral_constant_expression_p=*/false);
 
   return t;
 }
@@ -8629,10 +8643,13 @@
 		       tree args,
 		       tsubst_flags_t complain,
 		       tree in_decl,
-		       bool function_p)
+		       bool function_p,
+		       bool integral_constant_expression_p)
 {
-#define RECUR(NODE) \
-  tsubst_copy_and_build (NODE, args, complain, in_decl, /*function_p=*/false)
+#define RECUR(NODE)						\
+  tsubst_copy_and_build (NODE, args, complain, in_decl, 	\
+			 /*function_p=*/false,			\
+			 integral_constant_expression_p)
 
   tree op1;
 
@@ -8667,7 +8684,7 @@
 
 	decl = finish_id_expression (t, decl, NULL_TREE,
 				     &idk,
-				     /*integral_constant_expression_p=*/false,
+				     integral_constant_expression_p,
 				     /*allow_non_integral_constant_expression_p=*/false,
 				     &non_integral_constant_expression_p,
 				     /*template_p=*/false,
@@ -8730,30 +8747,42 @@
 	 RECUR (TREE_OPERAND (t, 0)));
 
     case CAST_EXPR:
-      return build_functional_cast
-	(tsubst (TREE_TYPE (t), args, complain, in_decl),
-	 RECUR (TREE_OPERAND (t, 0)));
-
     case REINTERPRET_CAST_EXPR:
-      return build_reinterpret_cast
-	(tsubst (TREE_TYPE (t), args, complain, in_decl),
-	 RECUR (TREE_OPERAND (t, 0)));
-
     case CONST_CAST_EXPR:
-      return build_const_cast
-	(tsubst (TREE_TYPE (t), args, complain, in_decl),
-	 RECUR (TREE_OPERAND (t, 0)));
-
     case DYNAMIC_CAST_EXPR:
-      return build_dynamic_cast
-	(tsubst (TREE_TYPE (t), args, complain, in_decl),
-	 RECUR (TREE_OPERAND (t, 0)));
-
     case STATIC_CAST_EXPR:
-      return build_static_cast
-	(tsubst (TREE_TYPE (t), args, complain, in_decl),
-	 RECUR (TREE_OPERAND (t, 0)));
+      {
+	tree type;
+	tree op;
 
+	type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	if (integral_constant_expression_p
+	    && !cast_valid_in_integral_constant_expression_p (type))
+	  {
+	    error ("a cast to a type other than an integral or "
+		   "enumeration type cannot appear in a constant-expression");
+	    return error_mark_node; 
+	  }
+
+	op = RECUR (TREE_OPERAND (t, 0));
+
+	switch (TREE_CODE (t))
+	  {
+	  case CAST_EXPR:
+	    return build_functional_cast (type, op);
+	  case REINTERPRET_CAST_EXPR:
+	    return build_reinterpret_cast (type, op);
+	  case CONST_CAST_EXPR:
+	    return build_const_cast (type, op);
+	  case DYNAMIC_CAST_EXPR:
+	    return build_dynamic_cast (type, op);
+	  case STATIC_CAST_EXPR:
+	    return build_static_cast (type, op);
+	  default:
+	    gcc_unreachable ();
+	  }
+      }
+
     case POSTDECREMENT_EXPR:
     case POSTINCREMENT_EXPR:
       op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
@@ -8843,7 +8872,9 @@
       else
 	{
 	  ++skip_evaluation;
-	  op1 = RECUR (op1);
+	  op1 = tsubst_copy_and_build (op1, args, complain, in_decl,
+				       /*function_p=*/false,
+				       /*integral_constant_expression_p=*/false);
 	  --skip_evaluation;
 	}
       if (TYPE_P (op1))
@@ -8928,7 +8959,8 @@
 	    
 	    function = tsubst_copy_and_build (function, args, complain,
 					      in_decl,
-					      !qualified_p);
+					      !qualified_p,
+					      integral_constant_expression_p);
 	    if (BASELINK_P (function))
 	      qualified_p = true;
 	  }
@@ -9173,7 +9205,8 @@
 	tree stmt_expr = begin_stmt_expr ();
 
 	cur_stmt_expr = stmt_expr;
-	tsubst_expr (STMT_EXPR_STMT (t), args, complain, in_decl);
+	tsubst_expr (STMT_EXPR_STMT (t), args, complain, in_decl,
+		     integral_constant_expression_p);
 	stmt_expr = finish_stmt_expr (stmt_expr, false);
 	cur_stmt_expr = old_stmt_expr;
 
@@ -11668,7 +11701,8 @@
   else if (TREE_CODE (decl) == VAR_DECL)
     DECL_INITIAL (decl) =
       tsubst_expr (DECL_INITIAL (code_pattern), args,
-		   tf_error, DECL_TI_TEMPLATE (decl));
+		   tf_error, DECL_TI_TEMPLATE (decl),
+		   /*integral_constant_expression_p=*/false);
   else
     gcc_unreachable ();
 
@@ -11917,7 +11951,8 @@
 	  push_nested_class (DECL_CONTEXT (d));
 	  init = tsubst_expr (DECL_INITIAL (code_pattern), 
 			      args,
-			      tf_error | tf_warning, NULL_TREE);
+			      tf_error | tf_warning, NULL_TREE,
+			      /*integral_constant_expression_p=*/false);
 	  DECL_INITIAL (d) = init;
 	  cp_finish_decl (d, init, /*init_const_expr_p=*/false,
 			  /*asmspec_tree=*/NULL_TREE,
@@ -12040,7 +12075,8 @@
 
       /* Substitute into the body of the function.  */
       tsubst_expr (DECL_SAVED_TREE (code_pattern), args,
-		   tf_error | tf_warning, tmpl);
+		   tf_error | tf_warning, tmpl,
+		   /*integral_constant_expression_p=*/false);
 
       /* We don't need the local specializations any more.  */
       htab_delete (local_specializations);
@@ -12194,7 +12230,8 @@
 	in_base_initializer = 1;
 
       init = tsubst_expr (TREE_VALUE (t), argvec, tf_error | tf_warning,
-			  NULL_TREE);
+			  NULL_TREE,
+			  /*integral_constant_expression_p=*/false);
       in_base_initializer = 0;
 
       if (decl)
@@ -12239,7 +12276,8 @@
 	 CONST_DECL, not the corresponding INTEGER_CST.  */
       value = tsubst_expr (DECL_INITIAL (decl),
 			   args, tf_error | tf_warning,
-			   NULL_TREE);
+			   NULL_TREE,
+			   /*integral_constant_expression_p=*/true);
 
       /* Give this enumeration constant the correct access.  */
       set_current_access_from_decl (decl);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]