+2002-02-22 Nathan Sidwell <nathan@codesourcery.com>
+
+ PR c++/2645, DR 295
+ * cp-tree.h (tsubst_flags_t): Add tf_ignore_bad_quals,
+ tf_keep_type_decl.
+ (make_typename_type): Use tsubst_flags_t.
+ * decl.c (make_typename_type): Adjust. Return non-artificial
+ TYPE_DECLs, if required.
+ (grokdeclarator): Simplify CVR qualification handling. Allow bad
+ qualifiers on typedef types.
+ * decl2.c (handle_class_head): Adjust make_typename_type call.
+ * parse.y (nested_name_specifier): Likewise.
+ (typename_sub0): Likewise.
+ (typename_sub1): Likewise.
+ * pt.c (convert_template_argument): Adjust make_typename_type
+ return value.
+ (tsubst): Adjust cp_build_qualified_type_real calls.
+ (check_cv_quals_for_unify): Cope with alowing bad qualifications
+ on template type parms.
+ (instantiate_decl): Recheck substitutions to give warnings on bad
+ qualifications.
+ * tree.c (cp_build_qualified_type_real): Use tf_allow_bad_quals.
+
2002-02-21 Aldy Hernandez <aldyh@redhat.com>
* cp/decl.c (duplicate_decls): Merge always_inline attribute.
tf_warning = 1 << 1, /* give warnings too */
tf_no_attributes = 1 << 2, /* ignore attributes on comparisons
(instantiate_type use) */
- tf_ptrmem_ok = 1 << 3 /* pointers to member ok (internal
+ tf_ignore_bad_quals = 1 << 3, /* ignore bad cvr qualifiers */
+ tf_keep_type_decl = 1 << 4, /* retain typedef type decls
+ (make_typename_type use) */
+ tf_ptrmem_ok = 1 << 5 /* pointers to member ok (internal
instantiate_type use) */
} tsubst_flags_t;
extern void set_namespace_binding PARAMS ((tree, tree, tree));
extern tree lookup_namespace_name PARAMS ((tree, tree));
extern tree build_typename_type PARAMS ((tree, tree, tree, tree));
-extern tree make_typename_type PARAMS ((tree, tree, int));
+extern tree make_typename_type PARAMS ((tree, tree, tsubst_flags_t));
extern tree make_unbound_class_template PARAMS ((tree, tree, int));
extern tree lookup_name_nonclass PARAMS ((tree));
extern tree lookup_function_nonclass PARAMS ((tree, tree));
/* Resolve `typename CONTEXT::NAME'. Returns an appropriate type,
unless an error occurs, in which case error_mark_node is returned.
- If COMPLAIN zero, don't complain about any errors that occur. */
+ If we locate a non-artificial TYPE_DECL and TF_KEEP_TYPE_DECL is
+ set, we return that, rather than the _TYPE it corresponds to, in
+ other cases we look through the type decl. If TF_ERROR is set,
+ complain about errors, otherwise be quiet. */
tree
make_typename_type (context, name, complain)
tree context, name;
- int complain;
+ tsubst_flags_t complain;
{
tree fullname;
{
/* We can get here from typename_sub0 in the explicit_template_type
expansion. Just fail. */
- if (complain)
+ if (complain & tf_error)
error ("no class template named `%#T' in `%#T'",
name, context);
return error_mark_node;
tmpl = lookup_field (context, name, 0, 0);
if (!tmpl || !DECL_CLASS_TEMPLATE_P (tmpl))
{
- if (complain)
+ if (complain & tf_error)
error ("no class template named `%#T' in `%#T'",
name, context);
return error_mark_node;
if (!IS_AGGR_TYPE (context))
{
- if (complain)
+ if (complain & tf_error)
error ("no type named `%#T' in `%#T'", name, context);
return error_mark_node;
}
t = lookup_field (context, name, 0, 1);
if (t)
- return TREE_TYPE (t);
+ {
+ if (DECL_ARTIFICIAL (t) || !(complain & tf_keep_type_decl))
+ t = TREE_TYPE (t);
+ return t;
+ }
}
}
there now or its never going to be. */
if (!uses_template_parms (context))
{
- if (complain)
+ if (complain & tf_error)
error ("no type named `%#T' in `%#T'", name, context);
return error_mark_node;
}
/* Resolve `CONTEXT::template NAME'. Returns an appropriate type,
unless an error occurs, in which case error_mark_node is returned.
- If COMPLAIN zero, don't complain about any errors that occur. */
+ If we locate a TYPE_DECL, we return that, rather than the _TYPE it
+ corresponds to. If COMPLAIN zero, don't complain about any errors
+ that occur. */
tree
make_unbound_class_template (context, name, complain)
tree spec;
tree type = NULL_TREE;
int longlong = 0;
- int constp;
- int restrictp;
- int volatilep;
int type_quals;
int virtualp, explicitp, friendp, inlinep, staticp;
int explicit_int = 0;
type = build_complex_type (type);
}
- if (sfk == sfk_conversion
- && (RIDBIT_SETP (RID_CONST, specbits)
- || RIDBIT_SETP (RID_VOLATILE, specbits)
- || RIDBIT_SETP (RID_RESTRICT, specbits)))
+ type_quals = TYPE_UNQUALIFIED;
+ if (RIDBIT_SETP (RID_CONST, specbits))
+ type_quals |= TYPE_QUAL_CONST;
+ if (RIDBIT_SETP (RID_VOLATILE, specbits))
+ type_quals |= TYPE_QUAL_VOLATILE;
+ if (RIDBIT_SETP (RID_RESTRICT, specbits))
+ type_quals |= TYPE_QUAL_RESTRICT;
+ if (sfk == sfk_conversion && type_quals != TYPE_UNQUALIFIED)
error ("qualifiers are not allowed on declaration of `operator %T'",
ctor_return_type);
- /* Set CONSTP if this declaration is `const', whether by
- explicit specification or via a typedef.
- Likewise for VOLATILEP. */
-
- constp = !! RIDBIT_SETP (RID_CONST, specbits) + CP_TYPE_CONST_P (type);
- restrictp =
- !! RIDBIT_SETP (RID_RESTRICT, specbits) + CP_TYPE_RESTRICT_P (type);
- volatilep =
- !! RIDBIT_SETP (RID_VOLATILE, specbits) + CP_TYPE_VOLATILE_P (type);
- type_quals = ((constp ? TYPE_QUAL_CONST : 0)
- | (restrictp ? TYPE_QUAL_RESTRICT : 0)
- | (volatilep ? TYPE_QUAL_VOLATILE : 0));
- type = cp_build_qualified_type (type, type_quals);
+ type_quals |= cp_type_quals (type);
+ type = cp_build_qualified_type_real
+ (type, type_quals, ((typedef_decl && !DECL_ARTIFICIAL (typedef_decl)
+ ? tf_ignore_bad_quals : 0) | tf_error | tf_warning));
+ /* We might have ignored or rejected some of the qualifiers. */
+ type_quals = cp_type_quals (type);
+
staticp = 0;
inlinep = !! RIDBIT_SETP (RID_INLINE, specbits);
virtualp = RIDBIT_SETP (RID_VIRTUAL, specbits);
{
register tree typemodlist;
int erred = 0;
-
- constp = 0;
- volatilep = 0;
- restrictp = 0;
+ int constp = 0;
+ int volatilep = 0;
+ int restrictp = 0;
+
for (typemodlist = TREE_TYPE (declarator); typemodlist;
typemodlist = TREE_CHAIN (typemodlist))
{
tree qualifier = TREE_VALUE (typemodlist);
if (qualifier == ridpointers[(int) RID_CONST])
- constp++;
+ {
+ constp++;
+ type_quals |= TYPE_QUAL_CONST;
+ }
else if (qualifier == ridpointers[(int) RID_VOLATILE])
- volatilep++;
+ {
+ volatilep++;
+ type_quals |= TYPE_QUAL_VOLATILE;
+ }
else if (qualifier == ridpointers[(int) RID_RESTRICT])
- restrictp++;
+ {
+ restrictp++;
+ type_quals |= TYPE_QUAL_RESTRICT;
+ }
else if (!erred)
{
erred = 1;
pedwarn ("duplicate `volatile'");
if (restrictp > 1)
pedwarn ("duplicate `restrict'");
-
- type_quals = ((constp ? TYPE_QUAL_CONST : 0)
- | (restrictp ? TYPE_QUAL_RESTRICT : 0)
- | (volatilep ? TYPE_QUAL_VOLATILE : 0));
- if (TREE_CODE (declarator) == ADDR_EXPR
- && (constp || volatilep))
- {
- if (constp)
- pedwarn ("discarding `const' applied to a reference");
- if (volatilep)
- pedwarn ("discarding `volatile' applied to a reference");
- type_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
- }
type = cp_build_qualified_type (type, type_quals);
+ type_quals = cp_type_quals (type);
}
declarator = TREE_OPERAND (declarator, 0);
ctype = NULL_TREE;
else if (type_quals & TYPE_QUAL_CONST)
{
error ("const `%s' cannot be declared `mutable'", name);
- RIDBIT_RESET (RID_MUTABLE, specbits);
+ RIDBIT_RESET (RID_MUTABLE, specbits);
}
}
{
/* According to the suggested resolution of core issue
180, 'typename' is assumed after a class-key. */
- decl = make_typename_type (scope, id, 1);
+ decl = make_typename_type (scope, id, tf_error);
if (decl != error_mark_node)
decl = TYPE_MAIN_DECL (decl);
else
{ $$ = $2; }
| nested_name_specifier TEMPLATE explicit_template_type SCOPE
{ got_scope = $$
- = make_typename_type ($1, $3, /*complain=*/1); }
+ = make_typename_type ($1, $3, tf_error); }
/* Error handling per Core 125. */
| nested_name_specifier IDENTIFIER SCOPE
{ got_scope = $$
- = make_typename_type ($1, $2, /*complain=*/1); }
+ = make_typename_type ($1, $2, tf_error); }
| nested_name_specifier PTYPENAME SCOPE
{ got_scope = $$
- = make_typename_type ($1, $2, /*complain=*/1); }
+ = make_typename_type ($1, $2, tf_error); }
;
/* Why the @#$%^& do type_name and notype_identifier need to be expanded
typename_sub1 identifier %prec EMPTY
{
if (TYPE_P ($1))
- $$ = make_typename_type ($1, $2, /*complain=*/1);
+ $$ = make_typename_type ($1, $2, tf_error);
else if (TREE_CODE ($2) == IDENTIFIER_NODE)
error ("`%T' is not a class or namespace", $2);
else
| typename_sub1 template_type %prec EMPTY
{ $$ = TREE_TYPE ($2); }
| typename_sub1 explicit_template_type %prec EMPTY
- { $$ = make_typename_type ($1, $2, /*complain=*/1); }
+ { $$ = make_typename_type ($1, $2, tf_error); }
| typename_sub1 TEMPLATE explicit_template_type %prec EMPTY
- { $$ = make_typename_type ($1, $3, /*complain=*/1); }
+ { $$ = make_typename_type ($1, $3, tf_error); }
;
typename_sub1:
| typename_sub1 typename_sub2
{
if (TYPE_P ($1))
- $$ = make_typename_type ($1, $2, /*complain=*/1);
+ $$ = make_typename_type ($1, $2, tf_error);
else if (TREE_CODE ($2) == IDENTIFIER_NODE)
error ("`%T' is not a class or namespace", $2);
else
}
| typename_sub1 explicit_template_type SCOPE
{ got_scope = $$
- = make_typename_type ($1, $2, /*complain=*/1); }
+ = make_typename_type ($1, $2, tf_error); }
| typename_sub1 TEMPLATE explicit_template_type SCOPE
{ got_scope = $$
- = make_typename_type ($1, $3, /*complain=*/1); }
+ = make_typename_type ($1, $3, tf_error); }
;
/* This needs to return a TYPE_DECL for simple names so that we don't
arg = make_typename_type (TREE_OPERAND (arg, 0),
TREE_OPERAND (arg, 1),
complain & tf_error);
- if (TREE_CODE (arg) == TYPE_DECL)
- arg = TREE_TYPE (arg);
is_type = 1;
}
if (is_type != requires_type)
my_friendly_assert (TYPE_P (arg), 0);
return cp_build_qualified_type_real
(arg, cp_type_quals (arg) | cp_type_quals (t),
- complain);
+ complain | tf_ignore_bad_quals);
}
else if (TREE_CODE (t) == BOUND_TEMPLATE_TEMPLATE_PARM)
{
if (cp_type_quals (t))
{
r = tsubst (TYPE_MAIN_VARIANT (t), args, complain, in_decl);
- r = cp_build_qualified_type_real
- (r, cp_type_quals (t), complain);
+ r = cp_build_qualified_type_real
+ (r, cp_type_quals (t),
+ complain | (TREE_CODE (t) == TEMPLATE_TYPE_PARM
+ ? tf_ignore_bad_quals : 0));
}
else
{
}
}
- f = make_typename_type (ctx, f, complain & tf_error);
+ f = make_typename_type (ctx, f,
+ (complain & tf_error) | tf_keep_type_decl);
if (f == error_mark_node)
return f;
- return cp_build_qualified_type_real
- (f, cp_type_quals (f) | cp_type_quals (t), complain);
+ if (TREE_CODE (f) == TYPE_DECL)
+ {
+ complain |= tf_ignore_bad_quals;
+ f = TREE_TYPE (f);
+ }
+
+ return cp_build_qualified_type_real
+ (f, cp_type_quals (f) | cp_type_quals (t), complain);
}
case UNBOUND_CLASS_TEMPLATE:
tree arg;
tree parm;
{
+ int arg_quals = cp_type_quals (arg);
+ int parm_quals = cp_type_quals (parm);
+
+ if (TREE_CODE (parm) == TEMPLATE_TYPE_PARM)
+ {
+ /* If the cvr quals of parm will not unify with ARG, they'll be
+ ignored in instantiation, so we have to do the same here. */
+ if (TREE_CODE (arg) == REFERENCE_TYPE
+ || TREE_CODE (arg) == FUNCTION_TYPE
+ || TREE_CODE (arg) == METHOD_TYPE)
+ parm_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ if (!POINTER_TYPE_P (arg) &&
+ TREE_CODE (arg) != TEMPLATE_TYPE_PARM)
+ parm_quals &= ~TYPE_QUAL_RESTRICT;
+ }
+
if (!(strict & (UNIFY_ALLOW_MORE_CV_QUAL | UNIFY_ALLOW_OUTER_MORE_CV_QUAL))
- && !at_least_as_qualified_p (arg, parm))
+ && (arg_quals & parm_quals) != parm_quals)
return 0;
if (!(strict & (UNIFY_ALLOW_LESS_CV_QUAL | UNIFY_ALLOW_OUTER_LESS_CV_QUAL))
- && !at_least_as_qualified_p (parm, arg))
+ && (parm_quals & arg_quals) != arg_quals)
return 0;
return 1;
import_export_decl (d);
}
+ if (!defer_ok)
+ {
+ /* Recheck the substitutions to obtain any warning messages
+ about ignoring cv qualifiers. */
+ tree gen = DECL_TEMPLATE_RESULT (gen_tmpl);
+ tree type = TREE_TYPE (gen);
+
+ if (TREE_CODE (gen) == FUNCTION_DECL)
+ {
+ tsubst (DECL_ARGUMENTS (gen), args, tf_error | tf_warning, d);
+ tsubst (TYPE_RAISES_EXCEPTIONS (type), args,
+ tf_error | tf_warning, d);
+ /* Don't simply tsubst the function type, as that will give
+ duplicate warnings about poor parameter qualifications.
+ The function arguments are the same as the decl_arguments
+ without the top level cv qualifiers. */
+ type = TREE_TYPE (type);
+ }
+ tsubst (type, args, tf_error | tf_warning, d);
+ }
+
if (TREE_CODE (d) == VAR_DECL && DECL_INITIALIZED_IN_CLASS_P (d)
&& DECL_INITIAL (d) == NULL_TREE)
/* We should have set up DECL_INITIAL in instantiate_class_template. */
/* Make a variant of TYPE, qualified with the TYPE_QUALS. Handles
arrays correctly. In particular, if TYPE is an array of T's, and
TYPE_QUALS is non-empty, returns an array of qualified T's.
- Errors are emitted under control of COMPLAIN. If COMPLAIN is zero,
- error_mark_node is returned for bad qualifiers. */
-
+
+ FLAGS determines how to deal with illformed qualifications. If
+ tf_ignore_bad_quals is set, then bad qualifications are dropped
+ (this is permitted if TYPE was introduced via a typedef or template
+ type parameter). If bad qualifications are dropped and tf_warning
+ is set, then a warning is issued for non-const qualifications. If
+ tf_ignore_bad_quals is not set and tf_error is not set, we
+ return error_mark_node. Otherwise, we issue an error, and ignore
+ the qualifications.
+
+ Qualification of a reference type is valid when the reference came
+ via a typedef or template type argument. [dcl.ref] No such
+ dispensation is provided for qualifying a function type. [dcl.fct]
+ DR 295 queries this and the proposed resolution brings it into line
+ with qualifiying a reference. We implement the DR. We also behave
+ in a similar manner for restricting non-pointer types. */
+
tree
cp_build_qualified_type_real (type, type_quals, complain)
tree type;
tsubst_flags_t complain;
{
tree result;
+ int bad_quals = TYPE_UNQUALIFIED;
if (type == error_mark_node)
return type;
if (type_quals == cp_type_quals (type))
return type;
- /* A restrict-qualified pointer type must be a pointer (or reference)
+ /* A reference, fucntion or method type shall not be cv qualified.
+ [dcl.ref], [dct.fct] */
+ if (type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)
+ && (TREE_CODE (type) == REFERENCE_TYPE
+ || TREE_CODE (type) == FUNCTION_TYPE
+ || TREE_CODE (type) == METHOD_TYPE))
+ {
+ bad_quals |= type_quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ type_quals &= ~(TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE);
+ }
+
+ /* A restrict-qualified type must be a pointer (or reference)
to object or incomplete type. */
if ((type_quals & TYPE_QUAL_RESTRICT)
&& TREE_CODE (type) != TEMPLATE_TYPE_PARM
- && (!POINTER_TYPE_P (type)
- || TYPE_PTRMEM_P (type)
- || TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE))
+ && TREE_CODE (type) != TYPENAME_TYPE
+ && !POINTER_TYPE_P (type))
{
- if (complain & tf_error)
- error ("`%T' cannot be `restrict'-qualified", type);
- else
- return error_mark_node;
-
+ bad_quals |= TYPE_QUAL_RESTRICT;
type_quals &= ~TYPE_QUAL_RESTRICT;
}
- if (type_quals != TYPE_UNQUALIFIED
- && TREE_CODE (type) == FUNCTION_TYPE)
+ if (bad_quals == TYPE_UNQUALIFIED)
+ /*OK*/;
+ else if (!(complain & (tf_error | tf_ignore_bad_quals)))
+ return error_mark_node;
+ else
{
- if (complain & tf_error)
- error ("`%T' cannot be `const'-, `volatile'-, or `restrict'-qualified", type);
- else
- return error_mark_node;
- type_quals = TYPE_UNQUALIFIED;
+ if (complain & tf_ignore_bad_quals)
+ /* We're not going to warn about constifying things that can't
+ be constified. */
+ bad_quals &= ~TYPE_QUAL_CONST;
+ if (bad_quals)
+ {
+ tree bad_type = build_qualified_type (ptr_type_node, bad_quals);
+
+ if (!(complain & tf_ignore_bad_quals))
+ error ("`%V' qualifiers cannot be applied to `%T'",
+ bad_type, type);
+ else if (complain & tf_warning)
+ warning ("ignoring `%V' qualifiers on `%T'", bad_type, type);
+ }
}
- else if (TREE_CODE (type) == ARRAY_TYPE)
+
+ if (TREE_CODE (type) == ARRAY_TYPE)
{
/* In C++, the qualification really applies to the array element
type. Obtain the appropriately qualified element type. */
{
/* For a pointer-to-member type, we can't just return a
cv-qualified version of the RECORD_TYPE. If we do, we
- haven't change the field that contains the actual pointer to
+ haven't changed the field that contains the actual pointer to
a method, and so TYPE_PTRMEMFUNC_FN_TYPE will be wrong. */
tree t;
t = cp_build_qualified_type_real (t, type_quals, complain);
return build_ptrmemfunc_type (t);
}
-
+
/* Retrieve (or create) the appropriately qualified variant. */
result = build_qualified_type (type, type_quals);
+2002-02-22 Nathan Sidwell <nathan@codesourcery.com>
+
+ * g++.dg/template/qualttp19.C: New test.
+ * g++.dg/template/qualttp20.C: New test.
+ * g++.old-deja/g++.jason/report.C: Adjust expected errors
+ * g++.old-deja/g++.other/qual1.C: Likewise.
+
2002-02-21 Aldy Hernandez <aldyh@redhat.com>
* gcc.dg/attr-alwaysinline.c: New.
--- /dev/null
+// { dg-do compile }
+
+// Copyright (C) 2001 Free Software Foundation, Inc.
+// Contributed by Nathan Sidwell 15 Dec 2001 <nathan@codesourcery.com>
+
+// PR 2645
+
+template <typename T>
+struct call_traits
+{
+ public:
+ typedef T type_less_spec;
+};
+
+template <typename T>
+struct call_traits<T&>
+{
+ typedef T type_more_spec;
+};
+
+
+int main()
+{
+ int num;
+
+ // Two typedefs lead to the instant. of the less spec. ("wrong") template
+ typedef int& r_type;
+ typedef const r_type cr_type;
+ call_traits<cr_type>::type_less_spec var = num; // { dg-error "" "" }
+
+ // The explicit type leads to the instantiation of the "correct" one
+ call_traits<const int&>::type_more_spec var2 = num;
+
+ // As happen with a single typedef!
+ typedef const int& std_cr_type;
+ call_traits<std_cr_type>::type_more_spec var3 = num;
+
+
+ // As happen, indeed, without the cv-qualifier
+ call_traits<r_type>::type_more_spec var4;
+}
--- /dev/null
+// { dg-do compile }
+
+// Copyright (C) 2001 Free Software Foundation, Inc.
+// Contributed by Nathan Sidwell 15 Dec 2001 <nathan@codesourcery.com>
+
+// PR 2645
+
+struct AS
+{
+ typedef void (myT) ();
+ struct L {};
+
+};
+
+
+template <typename T> struct B1 : T
+{
+ typedef typename T::L __restrict__ r;// { dg-error "`__restrict' qualifiers cannot" "" }
+ typedef typename T::myT __restrict__ p;// { dg-warning "ignoring `__restrict'" "" }
+
+ typedef typename T::myT volatile *myvolatile; // { dg-warning "ignoring `volatile'" "" }
+ typename T::myT volatile *a; // { dg-warning "ignoring `volatile'" "" }
+ myvolatile b; // { dg-bogus "ignoring `volatile'" "" { xfail *-*-* } }
+};
+template <typename T> struct B2 : T
+{
+ typedef typename T::myT const *myconst;
+ typename T::myT const *a;
+ myconst b;
+};
+
+B1<AS> b1; // { dg-error "instantiated" "" }
+B2<AS> b2;
// GROUPS passed error-reporting
// Special g++ Options: -Wreturn-type
+// DR 295 allows qualification via typedef
+
template <char C>
class badoo
{
};
typedef int const * bart ();
-typedef bart const * const * bar2; // ERROR - qualifiers
+typedef bart const * const * bar2; // ok - constifying qualifiers
+typedef bart volatile * const * bar2v; // WARNING - qualifiers
bar2 baz (X::Y y)
{ // ERROR - in this context
// Origin: Benjamin Pflugmann <philemon@spin.de>
// Special g++ Options: -O
+// DR 295 allows qualification via typedef
+
typedef const char *(func_type)();
class
{
public:
func_type *Function;
- const func_type* function(void) { return Function; } // ERROR - qualifiers
+ const func_type* function(void) { return Function; } // ok constifying
+ volatile func_type* functionv(void); // WARNING - qualifier
} action;
void work(const char *source)