aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Mitchell <mark@markmitchell.com>1998-10-03 23:11:19 +0000
committerMark Mitchell <mmitchel@gcc.gnu.org>1998-10-03 23:11:19 +0000
commit01240200ee6cb2f0a7b731faf63b8aa2a3049f61 (patch)
treef8109fee965c4dbf574d2437c97abec84280e237
parentd5698b07533c991d016c0c16e0fa0d87d1457565 (diff)
downloadgcc-01240200ee6cb2f0a7b731faf63b8aa2a3049f61.zip
gcc-01240200ee6cb2f0a7b731faf63b8aa2a3049f61.tar.gz
gcc-01240200ee6cb2f0a7b731faf63b8aa2a3049f61.tar.bz2
class.c (current_class_ptr, [...]): Clarify documentation.
* class.c (current_class_ptr, current_class_ref): Clarify documentation. * cvt.c (ocp_convert): Don't expect fold to remove all trivial NOP type conversions. * decl.c (decls_match): Use comptypes directly; ignore qualifiers on the DECL. (duplicate_decls): Remove qualifier checks on DECL. (grokdeclarator): Make the type built up include top-level qualifiers. * decl2.c (do_dtors): Fix spelling error. * error.c (dump_simple_decl): Don't look at qualifiers on the decl when printing type information. * init.c (build_new_1): Add documentation. Deal with the fact that type of allocated memory now contains qualifiers. * lex.c (is_global): Improve error-recovery. * sig.c (build_member_function_pointer): Don't cast away const on fields of sigtable_entry_type. * tree.c (lvalue_type): Don't look at top-level qualifiers on expressions. * typeck.c (decay_conversion): Likewise. (build_component_ref): Make sure the type of the COMPONENT_REF contains top-level qualifiers, as appropriate. Improve error-handling. (build_indirect_ref): Simplify. Don't strip top-level qualifiers. (build_array_ref): Likewise. (build_unary_op): Improve error-recovery. (unary_complex_lvalue): Make taking the address a bound member function an error, not a sorry. (build_conditional_expr): Look at the type qualifiers, not the qualifiers on the expression itself. From-SVN: r22802
-rw-r--r--gcc/cp/ChangeLog33
-rw-r--r--gcc/cp/class.c5
-rw-r--r--gcc/cp/cvt.c16
-rw-r--r--gcc/cp/decl.c31
-rw-r--r--gcc/cp/decl2.c2
-rw-r--r--gcc/cp/error.c1
-rw-r--r--gcc/cp/init.c18
-rw-r--r--gcc/cp/lex.c3
-rw-r--r--gcc/cp/sig.c2
-rw-r--r--gcc/cp/tree.c3
-rw-r--r--gcc/cp/typeck.c134
-rw-r--r--gcc/testsuite/g++.old-deja/g++.pt/overload3.C18
12 files changed, 172 insertions, 94 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 8604334..7634176 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,36 @@
+1998-10-03 Mark Mitchell <mark@markmitchell.com>
+
+ * class.c (current_class_ptr, current_class_ref): Clarify
+ documentation.
+ * cvt.c (ocp_convert): Don't expect fold to remove all trivial
+ NOP type conversions.
+ * decl.c (decls_match): Use comptypes directly; ignore
+ qualifiers on the DECL.
+ (duplicate_decls): Remove qualifier checks on DECL.
+ (grokdeclarator): Make the type built up include top-level
+ qualifiers.
+ * decl2.c (do_dtors): Fix spelling error.
+ * error.c (dump_simple_decl): Don't look at qualifiers on the decl
+ when printing type information.
+ * init.c (build_new_1): Add documentation. Deal with the fact
+ that type of allocated memory now contains qualifiers.
+ * lex.c (is_global): Improve error-recovery.
+ * sig.c (build_member_function_pointer): Don't cast away const
+ on fields of sigtable_entry_type.
+ * tree.c (lvalue_type): Don't look at top-level qualifiers on
+ expressions.
+ * typeck.c (decay_conversion): Likewise.
+ (build_component_ref): Make sure the type of the COMPONENT_REF
+ contains top-level qualifiers, as appropriate. Improve
+ error-handling.
+ (build_indirect_ref): Simplify. Don't strip top-level qualifiers.
+ (build_array_ref): Likewise.
+ (build_unary_op): Improve error-recovery.
+ (unary_complex_lvalue): Make taking the address a bound member
+ function an error, not a sorry.
+ (build_conditional_expr): Look at the type qualifiers, not the
+ qualifiers on the expression itself.
+
1998-10-03 Jason Merrill <jason@yorick.cygnus.com>
* decl2.c (merge_functions): Remove duplicates.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 8e7d715..b64a91a 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -48,8 +48,9 @@ static tree *current_class_base, *current_class_stack;
static int current_class_stacksize;
int current_class_depth;
-/* The current_class_ptr is the pointer to the current class.
- current_class_ref is the actual current class. */
+/* When we're processing a member function, current_class_ptr is the
+ PARM_DECL for the `this' pointer. The current_class_ref is an
+ expression for `*this'. */
tree current_class_ptr, current_class_ref;
/* The following two can be derived from the previous one */
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index c76b743..06c5290 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -664,9 +664,19 @@ ocp_convert (type, expr, convtype, flags)
&& TYPE_HAS_CONSTRUCTOR (type))
/* We need a new temporary; don't take this shortcut. */;
else if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (e)))
- /* Trivial conversion: cv-qualifiers do not matter on rvalues. */
- return fold (build1 (NOP_EXPR, type, e));
-
+ {
+ if (comptypes (type, TREE_TYPE (e), 1))
+ /* The call to fold will not always remove the NOP_EXPR as
+ might be expected, since if one of the types is a typedef;
+ the comparsion in fold is just equality of pointers, not a
+ call to comptypes. */
+ ;
+ else
+ e = build1 (NOP_EXPR, type, e);
+
+ return fold (e);
+ }
+
if (code == VOID_TYPE && (convtype & CONV_STATIC))
return build1 (CONVERT_EXPR, type, e);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a4ed7b9..71fe530 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2571,20 +2571,9 @@ decls_match (newdecl, olddecl)
types_match = TREE_TYPE (newdecl) == NULL_TREE;
else if (TREE_TYPE (newdecl) == NULL_TREE)
types_match = 0;
- /* Qualifiers must match, and they may be present on either, the type
- or the decl. */
- else if ((TREE_READONLY (newdecl)
- || TYPE_READONLY (TREE_TYPE (newdecl)))
- == (TREE_READONLY (olddecl)
- || TYPE_READONLY (TREE_TYPE (olddecl)))
- && (TREE_THIS_VOLATILE (newdecl)
- || TYPE_VOLATILE (TREE_TYPE (newdecl)))
- == (TREE_THIS_VOLATILE (olddecl)
- || TYPE_VOLATILE (TREE_TYPE (olddecl))))
- types_match = comptypes (TYPE_MAIN_VARIANT (TREE_TYPE (newdecl)),
- TYPE_MAIN_VARIANT (TREE_TYPE (olddecl)), 1);
else
- types_match = 0;
+ types_match = comptypes (TREE_TYPE (newdecl),
+ TREE_TYPE (olddecl), 1);
}
return types_match;
@@ -2908,13 +2897,6 @@ duplicate_decls (newdecl, olddecl)
olddecl);
}
}
- /* These bits are logically part of the type for non-functions. */
- else if (TREE_READONLY (newdecl) != TREE_READONLY (olddecl)
- || TREE_THIS_VOLATILE (newdecl) != TREE_THIS_VOLATILE (olddecl))
- {
- cp_pedwarn ("type qualifiers for `%#D'", newdecl);
- cp_pedwarn_at ("conflict with previous decl `%#D'", olddecl);
- }
}
/* If new decl is `static' and an `extern' was seen previously,
@@ -9084,7 +9066,7 @@ grokdeclarator (declarator, declspecs, decl_context, initialized, attrlist)
constp = !! RIDBIT_SETP (RID_CONST, specbits) + TYPE_READONLY (type);
volatilep = !! RIDBIT_SETP (RID_VOLATILE, specbits) + TYPE_VOLATILE (type);
- type = build_type_variant (type, 0, 0);
+ type = cp_build_type_variant (type, constp, volatilep);
staticp = 0;
inlinep = !! RIDBIT_SETP (RID_INLINE, specbits);
virtualp = RIDBIT_SETP (RID_VIRTUAL, specbits);
@@ -9817,6 +9799,7 @@ grokdeclarator (declarator, declspecs, decl_context, initialized, attrlist)
pedwarn ("discarding `volatile' applied to a reference");
constp = volatilep = 0;
}
+ type = cp_build_type_variant (type, constp, volatilep);
}
declarator = TREE_OPERAND (declarator, 0);
ctype = NULL_TREE;
@@ -10903,7 +10886,11 @@ grokparms (first_parm, funcdef_flag)
NULL_TREE);
if (! decl)
continue;
- type = TREE_TYPE (decl);
+
+ /* Top-level qualifiers on the parameters are
+ ignored for function types. */
+ type = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+
if (TREE_CODE (type) == VOID_TYPE)
decl = void_type_node;
else if (TREE_CODE (type) == METHOD_TYPE)
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 50eea9f..492688b 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -3192,7 +3192,7 @@ do_dtors (start)
Access control for implicit calls to the constructors,
the conversion functions, or the destructor called to
- create and destroy a static data member is per- formed as
+ create and destroy a static data member is performed as
if these calls appeared in the scope of the member's
class.
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index f06705e..fb6995f 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -667,7 +667,6 @@ dump_simple_decl (t, type, v)
{
dump_type_prefix (type, v, 0);
OB_PUTC (' ');
- dump_readonly_or_volatile (t, after);
}
if (DECL_CLASS_SCOPE_P (t))
{
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index c5ba2c4..b6a20ba 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2391,12 +2391,26 @@ build_new_1 (exp)
if (! TYPE_NEEDS_CONSTRUCTING (type)
&& ! IS_AGGR_TYPE (type) && ! has_array)
{
- /* New 2.0 interpretation: `new int (10)' means
- allocate an int, and initialize it with 10. */
+ /* We are processing something like `new int (10)', which
+ means allocate an int, and initialize it with 10. */
tree deref;
+ tree deref_type;
+ /* At present RVAL is a temporary variable, created to hold
+ the value from the call to `operator new'. We transform
+ it to (*RVAL = INIT, RVAL). */
rval = save_expr (rval);
deref = build_indirect_ref (rval, NULL_PTR);
+
+ /* Even for something like `new const int (10)' we must
+ allow the expression to be non-const while we do the
+ initialization. */
+ deref_type = TREE_TYPE (deref);
+ if (TYPE_READONLY (deref_type))
+ TREE_TYPE (deref)
+ = cp_build_type_variant (deref_type,
+ /*constp=*/0,
+ TYPE_VOLATILE (deref_type));
TREE_READONLY (deref) = 0;
if (TREE_CHAIN (init) != NULL_TREE)
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index ac4d8b49..d69dc6e 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -2853,6 +2853,9 @@ is_global (d)
while (1)
switch (TREE_CODE (d))
{
+ case ERROR_MARK:
+ return 1;
+
case OVERLOAD: d = OVL_FUNCTION (d); continue;
case TREE_LIST: d = TREE_VALUE (d); continue;
default:
diff --git a/gcc/cp/sig.c b/gcc/cp/sig.c
index 65350db..e70ec3f 100644
--- a/gcc/cp/sig.c
+++ b/gcc/cp/sig.c
@@ -321,7 +321,7 @@ build_member_function_pointer (member)
GNU_xref_ref (current_function_decl, name);
entry = build_lang_field_decl (FIELD_DECL, get_identifier (name),
- TYPE_MAIN_VARIANT (sigtable_entry_type));
+ sigtable_entry_type);
TREE_CONSTANT (entry) = 1;
TREE_READONLY (entry) = 1;
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index e66cbff..00202b3 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2571,9 +2571,6 @@ lvalue_type (arg)
tree type = TREE_TYPE (arg);
if (TREE_CODE (arg) == OVERLOAD)
type = unknown_type_node;
- else if (TREE_CODE (type) != ARRAY_TYPE)
- type = cp_build_type_variant
- (type, TREE_READONLY (arg), TREE_THIS_VOLATILE (arg));
return type;
}
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 40b5b7e..d0be08a 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1684,9 +1684,7 @@ decay_conversion (exp)
if (code == ARRAY_TYPE)
{
register tree adr;
- tree restype;
tree ptrtype;
- int constp, volatilep;
if (TREE_CODE (exp) == INDIRECT_REF)
{
@@ -1718,21 +1716,7 @@ decay_conversion (exp)
return error_mark_node;
}
- constp = volatilep = 0;
- if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'r'
- || TREE_CODE_CLASS (TREE_CODE (exp)) == 'd')
- {
- constp = TREE_READONLY (exp);
- volatilep = TREE_THIS_VOLATILE (exp);
- }
-
- restype = TREE_TYPE (type);
- if (TYPE_READONLY (type) || TYPE_VOLATILE (type)
- || constp || volatilep)
- restype = cp_build_type_variant (restype,
- TYPE_READONLY (type) || constp,
- TYPE_VOLATILE (type) || volatilep);
- ptrtype = build_pointer_type (restype);
+ ptrtype = build_pointer_type (TREE_TYPE (type));
if (TREE_CODE (exp) == VAR_DECL)
{
@@ -1954,14 +1938,24 @@ build_component_ref (datum, component, basetype_path, protect)
tree datum, component, basetype_path;
int protect;
{
- register tree basetype = TREE_TYPE (datum);
+ register tree basetype;
register enum tree_code code;
register tree field = NULL;
register tree ref;
+ tree field_type;
+ int constp;
+ int volatilep;
if (processing_template_decl)
return build_min_nt (COMPONENT_REF, datum, component);
+
+ if (datum == error_mark_node
+ || TREE_TYPE (datum) == error_mark_node)
+ return error_mark_node;
+ /* BASETYPE holds the type of the class containing the COMPONENT. */
+ basetype = TYPE_MAIN_VARIANT (TREE_TYPE (datum));
+
/* If DATUM is a COMPOUND_EXPR or COND_EXPR, move our reference
inside it. */
switch (TREE_CODE (datum))
@@ -1995,13 +1989,13 @@ build_component_ref (datum, component, basetype_path, protect)
if (code == REFERENCE_TYPE)
{
datum = convert_from_reference (datum);
- basetype = TREE_TYPE (datum);
+ basetype = TYPE_MAIN_VARIANT (TREE_TYPE (datum));
code = TREE_CODE (basetype);
}
if (TREE_CODE (datum) == OFFSET_REF)
{
datum = resolve_offset_ref (datum);
- basetype = TREE_TYPE (datum);
+ basetype = TYPE_MAIN_VARIANT (TREE_TYPE (datum));
code = TREE_CODE (basetype);
}
@@ -2086,7 +2080,7 @@ build_component_ref (datum, component, basetype_path, protect)
tree access, fndecl;
/* Unique, so use this one now. */
- basetype = TREE_PURPOSE (fndecls);
+ basetype = TYPE_MAIN_VARIANT (TREE_PURPOSE (fndecls));
fndecl = TREE_VALUE (fndecls);
access = compute_access (TREE_PURPOSE (fndecls), fndecl);
if (access == access_public_node)
@@ -2198,15 +2192,40 @@ build_component_ref (datum, component, basetype_path, protect)
}
}
- ref = fold (build (COMPONENT_REF, TREE_TYPE (field),
+ /* Compute the type of the field, as described in [expr.ref]. */
+ constp = 0;
+ volatilep = 0;
+ field_type = TREE_TYPE (field);
+ if (TREE_CODE (field_type) == REFERENCE_TYPE)
+ /* The standard says that the type of the result should be the
+ type referred to by the reference. But for now, at least, we
+ do the conversion from reference type later. */
+ ;
+ else
+ {
+ /* A field is const (volatile) if the enclosing object, or the
+ field itself, is const (volatile). But, a mutable field is
+ not const, even within a const object. */
+ constp = (!(DECL_LANG_SPECIFIC (field)
+ && DECL_MUTABLE_P (field))
+ && (TYPE_READONLY (field_type)
+ || TYPE_READONLY (TREE_TYPE (datum))));
+ volatilep = (TYPE_VOLATILE (field_type)
+ || TYPE_VOLATILE (TREE_TYPE (datum)));
+ if (!IS_SIGNATURE (field_type))
+ field_type = cp_build_type_variant (field_type, constp, volatilep);
+ }
+
+ ref = fold (build (COMPONENT_REF, field_type,
break_out_cleanups (datum), field));
- if (TREE_READONLY (datum) || TREE_READONLY (field))
+ /* Mark the expression const or volatile, as appropriate. Even
+ though we've dealt with the type above, we still have to mark the
+ expression itself. */
+ if (constp)
TREE_READONLY (ref) = 1;
- if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
+ else if (volatilep)
TREE_THIS_VOLATILE (ref) = 1;
- if (DECL_LANG_SPECIFIC (field) && DECL_MUTABLE_P (field))
- TREE_READONLY (ref) = 0;
return ref;
}
@@ -2270,29 +2289,34 @@ build_indirect_ref (ptr, errorstring)
if (TREE_CODE (type) == POINTER_TYPE || TREE_CODE (type) == REFERENCE_TYPE)
{
+ /* [expr.unary.op]
+
+ If the type of the expression is "pointer to T," the type
+ of the result is "T."
+
+ We must use the canonical variant because certain parts of
+ the back end, like fold, do pointer comparisons between
+ types. */
+ tree t = canonical_type_variant (TREE_TYPE (type));
+
if (TREE_CODE (pointer) == ADDR_EXPR
&& !flag_volatile
- && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_OPERAND (pointer, 0)))
- == TYPE_MAIN_VARIANT (TREE_TYPE (type)))
- && (TREE_READONLY (TREE_OPERAND (pointer, 0))
- == TYPE_READONLY (TREE_TYPE (type)))
- && (TREE_THIS_VOLATILE (TREE_OPERAND (pointer, 0))
- == TYPE_VOLATILE (TREE_TYPE (type))))
+ && comptypes (t, TREE_TYPE (TREE_OPERAND (pointer, 0)), 1))
+ /* The POINTER was something like `&x'. We simplify `*&x' to
+ `x'. */
return TREE_OPERAND (pointer, 0);
else
{
- tree t = TREE_TYPE (type);
- register tree ref = build1 (INDIRECT_REF,
- TYPE_MAIN_VARIANT (t), pointer);
+ tree ref = build1 (INDIRECT_REF, t, pointer);
/* We *must* set TREE_READONLY when dereferencing a pointer to const,
so that we get the proper error message if the result is used
to assign to. Also, &* is supposed to be a no-op. */
TREE_READONLY (ref) = TYPE_READONLY (t);
+ TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t);
TREE_SIDE_EFFECTS (ref)
- = (TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (pointer)
+ = (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (pointer)
|| flag_volatile);
- TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t);
return ref;
}
}
@@ -2403,23 +2427,16 @@ build_array_ref (array, idx)
warning ("subscripting array declared `register'");
}
- type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (array)));
+ type = TREE_TYPE (TREE_TYPE (array));
rval = build (ARRAY_REF, type, array, idx);
/* Array ref is const/volatile if the array elements are
or if the array is.. */
TREE_READONLY (rval)
- |= (TYPE_READONLY (TREE_TYPE (TREE_TYPE (array)))
- | TREE_READONLY (array));
+ |= (TYPE_READONLY (type) | TREE_READONLY (array));
TREE_SIDE_EFFECTS (rval)
- |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
- | TREE_SIDE_EFFECTS (array));
+ |= (TYPE_VOLATILE (type) | TREE_SIDE_EFFECTS (array));
TREE_THIS_VOLATILE (rval)
- |= (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (array)))
- /* This was added by rms on 16 Nov 91.
- It fixes vol struct foo *a; a->elts[1]
- in an inline function.
- Hope it doesn't break something else. */
- | TREE_THIS_VOLATILE (array));
+ |= (TYPE_VOLATILE (type) | TREE_THIS_VOLATILE (array));
return require_complete_type (fold (rval));
}
@@ -4720,7 +4737,8 @@ build_unary_op (code, xarg, noconvert)
&& !lvalue_or_else (arg, "unary `&'"))
return error_mark_node;
- argtype = build_pointer_type (argtype);
+ if (argtype != error_mark_node)
+ argtype = build_pointer_type (argtype);
if (mark_addressable (arg) == 0)
return error_mark_node;
@@ -4871,14 +4889,12 @@ unary_complex_lvalue (code, arg)
if (TREE_OPERAND (arg, 0)
&& (TREE_CODE (TREE_OPERAND (arg, 0)) != NOP_EXPR
|| (TREE_OPERAND (TREE_OPERAND (arg, 0), 0)
- != error_mark_node)))
- if (TREE_CODE (t) != FIELD_DECL)
- {
- /* Don't know if this should return address to just
- _DECL, or actual address resolved in this expression. */
- sorry ("address of bound pointer-to-member expression");
- return error_mark_node;
- }
+ != error_mark_node))
+ && TREE_CODE (t) != FIELD_DECL)
+ {
+ cp_error ("taking address of bound pointer-to-member expression");
+ return error_mark_node;
+ }
/* Add in the offset to the field. */
offset = convert (sizetype,
@@ -5115,8 +5131,8 @@ build_conditional_expr (ifexp, op1, op2)
if (type1 != type2)
type1 = cp_build_type_variant
(type1,
- TREE_READONLY (op1) || TREE_READONLY (op2),
- TREE_THIS_VOLATILE (op1) || TREE_THIS_VOLATILE (op2));
+ TYPE_READONLY (op1) || TYPE_READONLY (op2),
+ TYPE_VOLATILE (op1) || TYPE_VOLATILE (op2));
/* ??? This is a kludge to deal with the fact that
we don't sort out integers and enums properly, yet. */
result = fold (build (COND_EXPR, type1, ifexp, op1, op2));
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/overload3.C b/gcc/testsuite/g++.old-deja/g++.pt/overload3.C
new file mode 100644
index 0000000..a716917
--- /dev/null
+++ b/gcc/testsuite/g++.old-deja/g++.pt/overload3.C
@@ -0,0 +1,18 @@
+// Build don't link:
+
+template <class T>
+void g(T, T);
+
+template <class T>
+void g(int*, T);
+
+struct S
+{
+ void f() const
+ {
+ g(X, X+3);
+ }
+
+ double X[3];
+};
+