Commit d8f8dca1 by Mark Mitchell Committed by Mark Mitchell

class.c (pushclass): Tweak handling of class-level bindings.

	* class.c (pushclass): Tweak handling of class-level bindings.
	(resolve_address_of_overloaded_function): Update pointer-to-member
	handling.
	(instantiate_type): Likewise.
	* cvt.c (cp_convert_to_pointer): Likewise.
	* decl.c (pop_binding): Take the DECL to pop, not just the name.
	Deal with `struct stat' hack.
	(binding_level): Add to documentation.
	(push_binding): Clear BINDING_TYPE.
	(add_binding): New function.
	(push_local_binding): Use it.
	(push_class_binding): Likewise.
	(poplevel): Adjust calls to pop_binding.
	(poplevel_class): Likewise.
	(pushdecl): Adjust handling of TYPE_DECLs; add bindings for hidden
	declarations to current binding level.
	(push_class_level_binding): Likewise.
	(push_overloaded_decl): Adjust handling of OVERLOADs in local
	bindings.
	(lookup_namespace_name): Don't crash when confronted with a
	TEMPLATE_DECL.
	(lookup_name_real): Do `struct stat' hack in local binding
	contexts.
	(build_ptrmemfunc_type): Adjust documentation.
	(grokdeclarator): Don't avoid building real array types when
	processing templates unless really necessary.
	(finish_method): Adjust calls to pop_binding.
	* decl2.c (reparse_absdcl_as_expr): Recursively call ourselves,
	not reparse_decl_as_expr.
	(build_expr_from_tree): Deal with a template-id as the function to
	call in a METHOD_CALL_EXPR.
	* pt.c (convert_nontype_argument): Tweak pointer-to-member handling.
	(maybe_adjust_types_For_deduction): Don't do peculiar things with
	METHOD_TYPEs here.
	(resolve_overloaded_unification): Handle COMPONENT_REFs.  Build
	pointer-to-member types where necessary.
	* tree.c (build_cplus_array_type_1): Don't avoid building real
	array types when processing templates unless really necessary.
	(build_exception_variant): Compare the exception lists correctly.

From-SVN: r24314
parent 11b89622
1998-12-14 Mark Mitchell <mark@markmitchell.com>
* class.c (pushclass): Tweak handling of class-level bindings.
(resolve_address_of_overloaded_function): Update pointer-to-member
handling.
(instantiate_type): Likewise.
* cvt.c (cp_convert_to_pointer): Likewise.
* decl.c (pop_binding): Take the DECL to pop, not just the name.
Deal with `struct stat' hack.
(binding_level): Add to documentation.
(push_binding): Clear BINDING_TYPE.
(add_binding): New function.
(push_local_binding): Use it.
(push_class_binding): Likewise.
(poplevel): Adjust calls to pop_binding.
(poplevel_class): Likewise.
(pushdecl): Adjust handling of TYPE_DECLs; add bindings for hidden
declarations to current binding level.
(push_class_level_binding): Likewise.
(push_overloaded_decl): Adjust handling of OVERLOADs in local
bindings.
(lookup_namespace_name): Don't crash when confronted with a
TEMPLATE_DECL.
(lookup_name_real): Do `struct stat' hack in local binding
contexts.
(build_ptrmemfunc_type): Adjust documentation.
(grokdeclarator): Don't avoid building real array types when
processing templates unless really necessary.
(finish_method): Adjust calls to pop_binding.
* decl2.c (reparse_absdcl_as_expr): Recursively call ourselves,
not reparse_decl_as_expr.
(build_expr_from_tree): Deal with a template-id as the function to
call in a METHOD_CALL_EXPR.
* pt.c (convert_nontype_argument): Tweak pointer-to-member handling.
(maybe_adjust_types_For_deduction): Don't do peculiar things with
METHOD_TYPEs here.
(resolve_overloaded_unification): Handle COMPONENT_REFs. Build
pointer-to-member types where necessary.
* tree.c (build_cplus_array_type_1): Don't avoid building real
array types when processing templates unless really necessary.
(build_exception_variant): Compare the exception lists correctly.
1998-12-13 Mark Mitchell <mark@markmitchell.com> 1998-12-13 Mark Mitchell <mark@markmitchell.com>
* cp-tree.def (CPLUS_BINDING): Update documentation. * cp-tree.def (CPLUS_BINDING): Update documentation.
......
...@@ -4789,7 +4789,7 @@ pushclass (type, modify) ...@@ -4789,7 +4789,7 @@ pushclass (type, modify)
for (item = previous_class_values; item; item = TREE_CHAIN (item)) for (item = previous_class_values; item; item = TREE_CHAIN (item))
{ {
tree id = TREE_PURPOSE (item); tree id = TREE_PURPOSE (item);
tree decl = IDENTIFIER_CLASS_VALUE (id); tree decl = TREE_TYPE (item);
push_class_binding (id, decl); push_class_binding (id, decl);
if (TREE_CODE (decl) == TYPE_DECL) if (TREE_CODE (decl) == TYPE_DECL)
...@@ -4808,8 +4808,6 @@ pushclass (type, modify) ...@@ -4808,8 +4808,6 @@ pushclass (type, modify)
if (! (IS_AGGR_TYPE_CODE (TREE_CODE (tag_type)) if (! (IS_AGGR_TYPE_CODE (TREE_CODE (tag_type))
&& CLASSTYPE_IS_TEMPLATE (tag_type))) && CLASSTYPE_IS_TEMPLATE (tag_type)))
pushtag (TREE_PURPOSE (tags), tag_type, 0); pushtag (TREE_PURPOSE (tags), tag_type, 0);
else
pushdecl_class_level (CLASSTYPE_TI_TEMPLATE (tag_type));
} }
current_function_decl = this_fndecl; current_function_decl = this_fndecl;
...@@ -5028,11 +5026,12 @@ resolve_address_of_overloaded_function (target_type, ...@@ -5028,11 +5026,12 @@ resolve_address_of_overloaded_function (target_type,
interoperability with most_specialized_instantiation. */ interoperability with most_specialized_instantiation. */
tree matches = NULL_TREE; tree matches = NULL_TREE;
/* If the TARGET_TYPE is a pointer-to-a-method, we convert it to /* By the time we get here, we should be seeing only real
proper pointer-to-member type here. */ pointer-to-member types, not the internal POINTER_TYPE to
if (TREE_CODE (target_type) == POINTER_TYPE METHOD_TYPE representation. */
&& TREE_CODE (TREE_TYPE (target_type)) == METHOD_TYPE) my_friendly_assert (!(TREE_CODE (target_type) == POINTER_TYPE
target_type = build_ptrmemfunc_type (target_type); && (TREE_CODE (TREE_TYPE (target_type))
== METHOD_TYPE)), 0);
/* Check that the TARGET_TYPE is reasonable. */ /* Check that the TARGET_TYPE is reasonable. */
if (TYPE_PTRFN_P (target_type)) if (TYPE_PTRFN_P (target_type))
...@@ -5475,15 +5474,20 @@ instantiate_type (lhstype, rhs, complain) ...@@ -5475,15 +5474,20 @@ instantiate_type (lhstype, rhs, complain)
if (fn == error_mark_node) if (fn == error_mark_node)
return error_mark_node; return error_mark_node;
mark_addressable (fn); mark_addressable (fn);
TREE_TYPE (rhs) = lhstype;
TREE_OPERAND (rhs, 0) = fn; TREE_OPERAND (rhs, 0) = fn;
TREE_CONSTANT (rhs) = staticp (fn); TREE_CONSTANT (rhs) = staticp (fn);
if (TREE_CODE (lhstype) == POINTER_TYPE if (TYPE_PTRMEMFUNC_P (lhstype))
&& TREE_CODE (TREE_TYPE (lhstype)) == METHOD_TYPE)
{ {
build_ptrmemfunc_type (lhstype); /* We must use the POINTER_TYPE to METHOD_TYPE on RHS here
rhs = build_ptrmemfunc (lhstype, rhs, 0); so that build_ptrmemfunc knows that RHS we have is not
already a pointer-to-member constant. Instead, it is
just a ADDR_EXPR over a FUNCTION_DECL. */
TREE_TYPE (rhs) = TYPE_PTRMEMFUNC_FN_TYPE (lhstype);
rhs = build_ptrmemfunc (TREE_TYPE (rhs), rhs, 0);
} }
else
/* Here, things our simple; we have exactly what we need. */
TREE_TYPE (rhs) = lhstype;
} }
return rhs; return rhs;
......
...@@ -95,9 +95,6 @@ cp_convert_to_pointer (type, expr) ...@@ -95,9 +95,6 @@ cp_convert_to_pointer (type, expr)
} }
} }
if (TYPE_PTRMEMFUNC_P (type))
type = TYPE_PTRMEMFUNC_FN_TYPE (type);
/* Handle anachronistic conversions from (::*)() to cv void* or (*)(). */ /* Handle anachronistic conversions from (::*)() to cv void* or (*)(). */
if (TREE_CODE (type) == POINTER_TYPE if (TREE_CODE (type) == POINTER_TYPE
&& (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE && (TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE
...@@ -128,16 +125,14 @@ cp_convert_to_pointer (type, expr) ...@@ -128,16 +125,14 @@ cp_convert_to_pointer (type, expr)
intype = TREE_TYPE (expr); intype = TREE_TYPE (expr);
} }
if (TYPE_PTRMEMFUNC_P (intype))
intype = TYPE_PTRMEMFUNC_FN_TYPE (intype);
form = TREE_CODE (intype); form = TREE_CODE (intype);
if (form == POINTER_TYPE || form == REFERENCE_TYPE) if (POINTER_TYPE_P (intype))
{ {
intype = TYPE_MAIN_VARIANT (intype); intype = TYPE_MAIN_VARIANT (intype);
if (TYPE_MAIN_VARIANT (type) != intype if (TYPE_MAIN_VARIANT (type) != intype
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE && TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE
&& IS_AGGR_TYPE (TREE_TYPE (type)) && IS_AGGR_TYPE (TREE_TYPE (type))
&& IS_AGGR_TYPE (TREE_TYPE (intype)) && IS_AGGR_TYPE (TREE_TYPE (intype))
...@@ -181,12 +176,9 @@ cp_convert_to_pointer (type, expr) ...@@ -181,12 +176,9 @@ cp_convert_to_pointer (type, expr)
} }
} }
} }
if (TREE_CODE (TREE_TYPE (intype)) == METHOD_TYPE
&& TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE)
return build_ptrmemfunc (type, expr, 1);
if (TREE_CODE (TREE_TYPE (type)) == OFFSET_TYPE if (TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == OFFSET_TYPE
&& TREE_CODE (TREE_TYPE (intype)) == OFFSET_TYPE) && TREE_CODE (TREE_TYPE (intype)) == OFFSET_TYPE)
{ {
tree b1 = TYPE_OFFSET_BASETYPE (TREE_TYPE (type)); tree b1 = TYPE_OFFSET_BASETYPE (TREE_TYPE (type));
...@@ -205,10 +197,7 @@ cp_convert_to_pointer (type, expr) ...@@ -205,10 +197,7 @@ cp_convert_to_pointer (type, expr)
if (binfo && ! TREE_VIA_VIRTUAL (binfo)) if (binfo && ! TREE_VIA_VIRTUAL (binfo))
expr = size_binop (code, expr, BINFO_OFFSET (binfo)); expr = size_binop (code, expr, BINFO_OFFSET (binfo));
} }
else if (TYPE_PTRMEMFUNC_P (type))
if (TREE_CODE (TREE_TYPE (intype)) == METHOD_TYPE
|| (TREE_CODE (type) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE))
{ {
cp_error ("cannot convert `%E' from type `%T' to type `%T'", cp_error ("cannot convert `%E' from type `%T' to type `%T'",
expr, intype, type); expr, intype, type);
...@@ -219,6 +208,14 @@ cp_convert_to_pointer (type, expr) ...@@ -219,6 +208,14 @@ cp_convert_to_pointer (type, expr)
TREE_CONSTANT (rval) = TREE_CONSTANT (expr); TREE_CONSTANT (rval) = TREE_CONSTANT (expr);
return rval; return rval;
} }
else if (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))
return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, 1);
else if (TYPE_PTRMEMFUNC_P (intype))
{
cp_error ("cannot convert `%E' from type `%T' to type `%T'",
expr, intype, type);
return error_mark_node;
}
my_friendly_assert (form != OFFSET_TYPE, 186); my_friendly_assert (form != OFFSET_TYPE, 186);
...@@ -228,7 +225,7 @@ cp_convert_to_pointer (type, expr) ...@@ -228,7 +225,7 @@ cp_convert_to_pointer (type, expr)
if (integer_zerop (expr)) if (integer_zerop (expr))
{ {
if (TREE_CODE (TREE_TYPE (type)) == METHOD_TYPE) if (TYPE_PTRMEMFUNC_P (type))
return build_ptrmemfunc (type, expr, 0); return build_ptrmemfunc (type, expr, 0);
expr = build_int_2 (0, 0); expr = build_int_2 (0, 0);
TREE_TYPE (expr) = type; TREE_TYPE (expr) = type;
......
...@@ -3547,7 +3547,7 @@ reparse_absdcl_as_expr (type, decl) ...@@ -3547,7 +3547,7 @@ reparse_absdcl_as_expr (type, decl)
return build_functional_cast (type, NULL_TREE); return build_functional_cast (type, NULL_TREE);
/* recurse */ /* recurse */
decl = reparse_decl_as_expr (type, TREE_OPERAND (decl, 0)); decl = reparse_absdcl_as_expr (type, TREE_OPERAND (decl, 0));
decl = build_x_function_call (decl, NULL_TREE, current_class_ref); decl = build_x_function_call (decl, NULL_TREE, current_class_ref);
...@@ -3770,11 +3770,28 @@ build_expr_from_tree (t) ...@@ -3770,11 +3770,28 @@ build_expr_from_tree (t)
TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 1),
build_expr_from_tree (TREE_OPERAND (t, 2))); build_expr_from_tree (TREE_OPERAND (t, 2)));
} }
return build_method_call else
(build_expr_from_tree (TREE_OPERAND (t, 1)), {
TREE_OPERAND (t, 0), tree fn = TREE_OPERAND (t, 0);
build_expr_from_tree (TREE_OPERAND (t, 2)),
NULL_TREE, LOOKUP_NORMAL); /* We can get a TEMPLATE_ID_EXPR here on code like:
x->f<2>();
so we must resolve that. However, we can also get things
like a BIT_NOT_EXPR here, when referring to a destructor,
and things like that are not correctly resolved by
build_expr_from_tree. So, just use build_expr_from_tree
when we really need it. */
if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
fn = build_expr_from_tree (fn);
return build_method_call
(build_expr_from_tree (TREE_OPERAND (t, 1)),
fn,
build_expr_from_tree (TREE_OPERAND (t, 2)),
NULL_TREE, LOOKUP_NORMAL);
}
case CALL_EXPR: case CALL_EXPR:
if (TREE_CODE (TREE_OPERAND (t, 0)) == SCOPE_REF) if (TREE_CODE (TREE_OPERAND (t, 0)) == SCOPE_REF)
......
...@@ -2710,9 +2710,6 @@ convert_nontype_argument (type, expr) ...@@ -2710,9 +2710,6 @@ convert_nontype_argument (type, expr)
case RECORD_TYPE: case RECORD_TYPE:
{ {
tree fns;
tree fn;
if (!TYPE_PTRMEMFUNC_P (type)) if (!TYPE_PTRMEMFUNC_P (type))
/* This handles templates like /* This handles templates like
template<class T, T t> void f(); template<class T, T t> void f();
...@@ -2743,16 +2740,11 @@ convert_nontype_argument (type, expr) ...@@ -2743,16 +2740,11 @@ convert_nontype_argument (type, expr)
if (TREE_CODE (expr) != ADDR_EXPR) if (TREE_CODE (expr) != ADDR_EXPR)
return error_mark_node; return error_mark_node;
fns = TREE_OPERAND (expr, 0); expr = instantiate_type (type, expr, 0);
fn = instantiate_type (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (type)),
fns, 0);
if (fn == error_mark_node) if (expr == error_mark_node)
return error_mark_node; return error_mark_node;
expr = build_unary_op (ADDR_EXPR, fn, 0);
my_friendly_assert (same_type_p (type, TREE_TYPE (expr)), my_friendly_assert (same_type_p (type, TREE_TYPE (expr)),
0); 0);
return expr; return expr;
...@@ -6972,8 +6964,7 @@ maybe_adjust_types_for_deduction (strict, parm, arg) ...@@ -6972,8 +6964,7 @@ maybe_adjust_types_for_deduction (strict, parm, arg)
deduction. */ deduction. */
if (TREE_CODE (*arg) == ARRAY_TYPE) if (TREE_CODE (*arg) == ARRAY_TYPE)
*arg = build_pointer_type (TREE_TYPE (*arg)); *arg = build_pointer_type (TREE_TYPE (*arg));
else if (TREE_CODE (*arg) == FUNCTION_TYPE else if (TREE_CODE (*arg) == FUNCTION_TYPE)
|| TREE_CODE (*arg) == METHOD_TYPE)
*arg = build_pointer_type (*arg); *arg = build_pointer_type (*arg);
else else
*arg = TYPE_MAIN_VARIANT (*arg); *arg = TYPE_MAIN_VARIANT (*arg);
...@@ -7163,6 +7154,11 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict, ...@@ -7163,6 +7154,11 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict,
if (TREE_CODE (arg) == ADDR_EXPR) if (TREE_CODE (arg) == ADDR_EXPR)
arg = TREE_OPERAND (arg, 0); arg = TREE_OPERAND (arg, 0);
if (TREE_CODE (arg) == COMPONENT_REF)
/* Handle `&x' where `x' is some static or non-static member
function name. */
arg = TREE_OPERAND (arg, 1);
/* Strip baselink information. */ /* Strip baselink information. */
while (TREE_CODE (arg) == TREE_LIST) while (TREE_CODE (arg) == TREE_LIST)
arg = TREE_VALUE (arg); arg = TREE_VALUE (arg);
...@@ -7188,6 +7184,8 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict, ...@@ -7188,6 +7184,8 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict,
if (subargs) if (subargs)
{ {
elem = tsubst (TREE_TYPE (fn), subargs, NULL_TREE); elem = tsubst (TREE_TYPE (fn), subargs, NULL_TREE);
if (TREE_CODE (elem) == METHOD_TYPE)
elem = build_ptrmemfunc_type (build_pointer_type (elem));
good += try_one_overload (tparms, targs, tempargs, parm, elem, good += try_one_overload (tparms, targs, tempargs, parm, elem,
strict, sub_strict, explicit_mask); strict, sub_strict, explicit_mask);
} }
...@@ -7196,9 +7194,14 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict, ...@@ -7196,9 +7194,14 @@ resolve_overloaded_unification (tparms, targs, parm, arg, strict,
else if (TREE_CODE (arg) == OVERLOAD) else if (TREE_CODE (arg) == OVERLOAD)
{ {
for (; arg; arg = OVL_NEXT (arg)) for (; arg; arg = OVL_NEXT (arg))
good += try_one_overload (tparms, targs, tempargs, parm, {
TREE_TYPE (OVL_CURRENT (arg)), tree type = TREE_TYPE (OVL_CURRENT (arg));
strict, sub_strict, explicit_mask); if (TREE_CODE (type) == METHOD_TYPE)
type = build_ptrmemfunc_type (build_pointer_type (type));
good += try_one_overload (tparms, targs, tempargs, parm,
type,
strict, sub_strict, explicit_mask);
}
} }
else else
my_friendly_abort (981006); my_friendly_abort (981006);
......
...@@ -416,7 +416,7 @@ build_cplus_array_type_1 (elt_type, index_type) ...@@ -416,7 +416,7 @@ build_cplus_array_type_1 (elt_type, index_type)
saveable_obstack = &permanent_obstack; saveable_obstack = &permanent_obstack;
} }
if (processing_template_decl if (uses_template_parms (elt_type)
|| uses_template_parms (index_type)) || uses_template_parms (index_type))
{ {
t = make_node (ARRAY_TYPE); t = make_node (ARRAY_TYPE);
...@@ -1467,15 +1467,20 @@ build_exception_variant (type, raises) ...@@ -1467,15 +1467,20 @@ build_exception_variant (type, raises)
for (; v; v = TYPE_NEXT_VARIANT (v)) for (; v; v = TYPE_NEXT_VARIANT (v))
{ {
tree t;
tree u;
if (TYPE_QUALS (v) != type_quals) if (TYPE_QUALS (v) != type_quals)
continue; continue;
/* @@ This should do set equality, not exact match. */ for (t = TYPE_RAISES_EXCEPTIONS (v), u = raises;
if (simple_cst_list_equal (TYPE_RAISES_EXCEPTIONS (v), raises)) t != NULL_TREE && u != NULL_TREE;
/* List of exceptions raised matches previously found list. t = TREE_CHAIN (t), u = TREE_CHAIN (v))
if (!same_type_p (TREE_VALUE (t), TREE_VALUE (u)))
break;
@@ Nice to free up storage used in consing up the if (!t && !u)
@@ list of exceptions raised. */ /* There's a memory leak here; RAISES is not freed. */
return v; return v;
} }
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
// Posted by Trevor Taylor <ttaylor@powerup.com.au> // Posted by Trevor Taylor <ttaylor@powerup.com.au>
template<class T> struct A { template<class T> struct A {
void X() throw(T); // gets bogus error - previous decl - XFAIL *-*-* void X() throw(T);
}; };
template<class T> template<class T>
inline void A<T>::X() inline void A<T>::X()
throw(T) { } // gets bogus error - different throws - XFAIL *-*-* throw(T) { }
// Build don't link:
namespace N {
template <class T> struct S;
};
void f()
{
N::S(); // ERROR - invalid use of template
}
// Build don't link: // Build don't link:
// crash test - XFAIL *-*-*
// Simplified from bug report by Trevor Taylor <ttaylor@powerup.com.au> // Simplified from bug report by Trevor Taylor <ttaylor@powerup.com.au>
struct T { struct T {
int operator()(int) { } int operator()(int) { } // ERROR - candidate
}; };
int main() { int main() {
......
// Build don't link:
struct S {
int A;
struct A {
enum { a = 0 };
};
void f();
};
void S::f() {
A = A::a;
}
// Build don't link:
template<int N, class C>
class Bar {};
template<class C>
class Huh {};
template<int N>
void foo(const Bar<N,Huh<float[1]> > &x) {}
int main() {
foo(Bar<3,Huh<float[1]> >());
}
// Build don't link:
template <int i> class a
{
public :
int k;
template <int j> int f() const { return this->f<j-1>(); }
int g() const { return f<i>(); };
};
template <>
template <>
int a<2>::f<0>() const {
return 0;
}
int main()
{
a<2> x;
return x.g();
}
// Build don't run:
template <class T>
int f(int (*fn)(T))
{
return (*fn)(3);
}
struct S {
static int g(int) { return 1; }
static void g();
int h();
};
int S::h()
{
return f(&g);
}
int main()
{
S s;
if (s.h () != 1)
return 1;
}
// Build don't run:
template<class T,class T1>
int connect_to_method(T* receiver,
int (T1::*method)())
{
return (receiver->*method)();
}
class Gtk_Container
{
public:
int remove_callback() { return 1; }
void remove_callback(int);
int f();
};
int Gtk_Container::f()
{
return connect_to_method(this, &Gtk_Container::remove_callback);
}
int main()
{
Gtk_Container gc;
if (gc.f () != 1)
return 1;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment