This patch generalizes our existing functionality for deferring access
checking of typedefs when parsing a function or class template to now
defer all kinds of access checks until template instantiation time,
including member function and member object accesses.
Since all access checks eventually go through enforce_access, the main
component of this patch is new handling inside enforce_access to defer
the current access check if we're inside a template. The bulk of the
rest of the patch consists of removing now-unneeded code pertaining to
suppressing access checks inside templates or pertaining to
typedef-specific access handling. Renamings and other changes with no
functional impact have been split off into the followup patch.
gcc/cp/ChangeLog:
PR c++/41437
PR c++/47346
* call.c (enforce_access): Move to semantics.c.
* cp-tree.h (enforce_access): Delete.
(get_types_needing_access_check): Delete.
(add_typedef_to_current_template_for_access_check): Delete.
* decl.c (make_typename_type): Adjust accordingly. Use
check_accessibility_of_qualified_id instead of directly using
perform_or_defer_access_check.
* parser.c (cp_parser_template_declaration_after_parameters):
Don't push a dk_no_check access state when parsing a template.
* pt.c (get_types_needing_access_check): Delete.
(append_type_to_template_for_access_check_1): Delete.
(perform_typedefs_access_check): Adjust. If type_decl is a
FIELD_DECL, also check its DECL_CONTEXT for dependence. Use
tsubst_copy instead of tsubst to substitute into type_decl so
that we substitute into the DECL_CONTEXT of a FIELD_DECL.
(append_type_to_template_for_access_check): Delete.
* search.c (accessible_p): Remove the processing_template_decl
early exit.
* semantics.c (enforce_access): Moved from call.c. If we're
parsing a template and the access check failed, add the check to
TI_TYPEDEFS_NEEDING_ACCESS_CHECKING.
(perform_or_defer_access_check): Adjust comment.
(add_typedef_to_current_template_for_access_check): Delete.
(check_accessibility_of_qualified_id): Adjust accordingly.
Exit early if the scope is dependent.
gcc/testsuite/ChangeLog:
PR c++/41437
PR c++/47346
* g++.dg/cpp2a/concepts-using2.C: Adjust.
* g++.dg/lto/20081219_1.C: Adjust.
* g++.dg/lto/
20091002-1_0.C: Adjust.
* g++.dg/lto/pr65475c_0.C: Adjust.
* g++.dg/opt/dump1.C: Adjust.
* g++.dg/other/pr53574.C: Adjust.
* g++.dg/template/access30.C: New test.
* g++.dg/template/access31.C: New test.
* g++.dg/wrappers/wrapper-around-type-pack-expansion.C: Adjust.
libstdc++-v3/ChangeLog:
PR libstdc++/94003
* testsuite/20_util/is_constructible/94003.cc: New test.
}
}
-/* If the current scope isn't allowed to access DECL along
- BASETYPE_PATH, give an error. The most derived class in
- BASETYPE_PATH is the one used to qualify DECL. DIAG_DECL is
- the declaration to use in the error diagnostic. */
-
-bool
-enforce_access (tree basetype_path, tree decl, tree diag_decl,
- tsubst_flags_t complain, access_failure_info *afi)
-{
- gcc_assert (TREE_CODE (basetype_path) == TREE_BINFO);
-
- if (flag_new_inheriting_ctors
- && DECL_INHERITED_CTOR (decl))
- {
- /* 7.3.3/18: The additional constructors are accessible if they would be
- accessible when used to construct an object of the corresponding base
- class. */
- decl = strip_inheriting_ctors (decl);
- basetype_path = lookup_base (basetype_path, DECL_CONTEXT (decl),
- ba_any, NULL, complain);
- }
-
- if (!accessible_p (basetype_path, decl, true))
- {
- if (flag_new_inheriting_ctors)
- diag_decl = strip_inheriting_ctors (diag_decl);
- if (complain & tf_error)
- complain_about_access (decl, diag_decl, true);
- if (afi)
- afi->record_access_failure (basetype_path, decl, diag_decl);
- return false;
- }
-
- return true;
-}
-
/* Initialize a temporary of type TYPE with EXPR. The FLAGS are a
bitwise or of LOOKUP_* values. If any errors are warnings are
generated, set *DIAGNOSTIC_FN to "error" or "warning",
};
extern void complain_about_access (tree, tree, bool);
-extern bool enforce_access (tree, tree, tree,
- tsubst_flags_t,
- access_failure_info *afi = NULL);
extern void push_defarg_context (tree);
extern void pop_defarg_context (void);
extern tree convert_default_arg (tree, tree, tree, int,
extern bool check_for_bare_parameter_packs (tree, location_t = UNKNOWN_LOCATION);
extern tree build_template_info (tree, tree);
extern tree get_template_info (const_tree);
-extern vec<qualified_typedef_usage_t, va_gc> *get_types_needing_access_check (tree);
extern int template_class_depth (tree);
extern int is_specialization_of (tree, tree);
extern bool is_specialization_of_friend (tree, tree);
extern tree check_template_template_default_arg (tree);
extern bool expand_or_defer_fn_1 (tree);
extern void expand_or_defer_fn (tree);
-extern void add_typedef_to_current_template_for_access_check (tree, tree,
- location_t);
extern bool check_accessibility_of_qualified_id (tree, tree, tree, tsubst_flags_t);
extern tree finish_qualified_id_expr (tree, tree, bool, bool,
bool, bool, tsubst_flags_t);
return error_mark_node;
}
- if (!perform_or_defer_access_check (TYPE_BINFO (context), t, t, complain))
+ if (!check_accessibility_of_qualified_id (t, /*object_type=*/NULL_TREE,
+ context, complain))
return error_mark_node;
- /* If we are currently parsing a template and if T is a typedef accessed
- through CONTEXT then we need to remember and check access of T at
- template instantiation time. */
- add_typedef_to_current_template_for_access_check (t, context, input_location);
-
if (want_template)
return lookup_template_class (t, TREE_OPERAND (fullname, 1),
NULL_TREE, context,
decl = cp_parser_concept_definition (parser);
else
{
- /* There are no access checks when parsing a template, as we do not
- know if a specialization will be a friend. */
- push_deferring_access_checks (dk_no_check);
cp_token *token = cp_lexer_peek_token (parser->lexer);
decl = cp_parser_single_declaration (parser,
checks,
member_p,
/*explicit_specialization_p=*/false,
&friend_p);
- pop_deferring_access_checks ();
/* If this is a member template declaration, let the front
end know. */
static tree tsubst_copy (tree, tree, tsubst_flags_t, tree);
static tree tsubst_decl (tree, tree, tsubst_flags_t);
static void perform_typedefs_access_check (tree tmpl, tree targs);
-static void append_type_to_template_for_access_check_1 (tree, tree, tree,
- location_t);
static tree listify (tree);
static tree listify_autos (tree, tree);
static tree tsubst_template_parm (tree, tree, tsubst_flags_t);
return;
if (vec<qualified_typedef_usage_t, va_gc> *tdefs
- = get_types_needing_access_check (tmpl))
+ = TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (get_template_info (tmpl)))
FOR_EACH_VEC_ELT (*tdefs, i, iter)
{
tree type_decl = iter->typedef_decl;
if (!type_decl || !type_scope || !CLASS_TYPE_P (type_scope))
continue;
- if (uses_template_parms (type_decl))
- type_decl = tsubst (type_decl, targs, tf_error, NULL_TREE);
+ if (uses_template_parms (type_decl)
+ || (TREE_CODE (type_decl) == FIELD_DECL
+ && uses_template_parms (DECL_CONTEXT (type_decl))))
+ type_decl = tsubst_copy (type_decl, targs, tf_error, NULL_TREE);
if (uses_template_parms (type_scope))
type_scope = tsubst (type_scope, targs, tf_error, NULL_TREE);
return errors;
}
-/* For a given template T, return the vector of typedefs referenced
- in T for which access check is needed at T instantiation time.
- T is either a FUNCTION_DECL or a RECORD_TYPE.
- Those typedefs were added to T by the function
- append_type_to_template_for_access_check. */
-
-vec<qualified_typedef_usage_t, va_gc> *
-get_types_needing_access_check (tree t)
-{
- gcc_checking_assert ((CLASS_TYPE_P (t) || TREE_CODE (t) == FUNCTION_DECL));
-
- if (tree ti = get_template_info (t))
- if (TI_TEMPLATE (ti))
- return TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (ti);
-
- return NULL;
-}
-
-/* Append the typedef TYPE_DECL used in template T to a list of typedefs
- tied to T. That list of typedefs will be access checked at
- T instantiation time.
- T is either a FUNCTION_DECL or a RECORD_TYPE.
- TYPE_DECL is a TYPE_DECL node representing a typedef.
- SCOPE is the scope through which TYPE_DECL is accessed.
- LOCATION is the location of the usage point of TYPE_DECL.
-
- This function is a subroutine of
- append_type_to_template_for_access_check. */
-
-static void
-append_type_to_template_for_access_check_1 (tree t,
- tree type_decl,
- tree scope,
- location_t location)
-{
- qualified_typedef_usage_t typedef_usage;
- tree ti;
-
- if (!t || t == error_mark_node)
- return;
-
- gcc_assert ((TREE_CODE (t) == FUNCTION_DECL
- || CLASS_TYPE_P (t))
- && type_decl
- && TREE_CODE (type_decl) == TYPE_DECL
- && scope);
-
- if (!(ti = get_template_info (t)))
- return;
-
- gcc_assert (TI_TEMPLATE (ti));
-
- typedef_usage.typedef_decl = type_decl;
- typedef_usage.context = scope;
- typedef_usage.locus = location;
-
- vec_safe_push (TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (ti), typedef_usage);
-}
-
-/* Append TYPE_DECL to the template TEMPL.
- TEMPL is either a class type, a FUNCTION_DECL or a TEMPLATE_DECL.
- At TEMPL instanciation time, TYPE_DECL will be checked to see
- if it can be accessed through SCOPE.
- LOCATION is the location of the usage point of TYPE_DECL.
-
- e.g. consider the following code snippet:
-
- class C
- {
- typedef int myint;
- };
-
- template<class U> struct S
- {
- C::myint mi; // <-- usage point of the typedef C::myint
- };
-
- S<char> s;
-
- At S<char> instantiation time, we need to check the access of C::myint
- In other words, we need to check the access of the myint typedef through
- the C scope. For that purpose, this function will add the myint typedef
- and the scope C through which its being accessed to a list of typedefs
- tied to the template S. That list will be walked at template instantiation
- time and access check performed on each typedefs it contains.
- Note that this particular code snippet should yield an error because
- myint is private to C. */
-
-void
-append_type_to_template_for_access_check (tree templ,
- tree type_decl,
- tree scope,
- location_t location)
-{
- qualified_typedef_usage_t *iter;
- unsigned i;
-
- gcc_assert (type_decl && (TREE_CODE (type_decl) == TYPE_DECL));
-
- /* Make sure we don't append the type to the template twice. */
- if (vec<qualified_typedef_usage_t, va_gc> *tdefs
- = get_types_needing_access_check (templ))
- FOR_EACH_VEC_ELT (*tdefs, i, iter)
- if (iter->typedef_decl == type_decl && scope == iter->context)
- return;
-
- append_type_to_template_for_access_check_1 (templ, type_decl,
- scope, location);
-}
-
/* Recursively walk over && expressions searching for EXPR. Return a reference
to that expression. */
if (current_function_decl && DECL_THUNK_P (current_function_decl))
return 1;
- /* In a template declaration, we cannot be sure whether the
- particular specialization that is instantiated will be a friend
- or not. Therefore, all access checks are deferred until
- instantiation. However, PROCESSING_TEMPLATE_DECL is set in the
- parameter list for a template (because we may see dependent types
- in default arguments for template parameters), and access
- checking should be performed in the outermost parameter list. */
- if (processing_template_decl
- /* FIXME CWG has been talking about doing access checking in the context
- of the constraint-expression, rather than the constrained declaration,
- in which case we would want to remove this test. */
- && !processing_constraint_expression_p ()
- && (!processing_template_parmlist || processing_template_decl > 1))
- return 1;
-
tree otype = NULL_TREE;
if (!TYPE_P (type))
{
}
}
+/* If the current scope isn't allowed to access DECL along
+ BASETYPE_PATH, give an error, or if we're parsing a function or class
+ template, defer the access check to be performed at instantiation time.
+ The most derived class in BASETYPE_PATH is the one used to qualify DECL.
+ DIAG_DECL is the declaration to use in the error diagnostic. */
+
+static bool
+enforce_access (tree basetype_path, tree decl, tree diag_decl,
+ tsubst_flags_t complain, access_failure_info *afi = NULL)
+{
+ gcc_assert (TREE_CODE (basetype_path) == TREE_BINFO);
+
+ if (flag_new_inheriting_ctors
+ && DECL_INHERITED_CTOR (decl))
+ {
+ /* 7.3.3/18: The additional constructors are accessible if they would be
+ accessible when used to construct an object of the corresponding base
+ class. */
+ decl = strip_inheriting_ctors (decl);
+ basetype_path = lookup_base (basetype_path, DECL_CONTEXT (decl),
+ ba_any, NULL, complain);
+ }
+
+ tree cs = current_scope ();
+ if (processing_template_decl
+ && (CLASS_TYPE_P (cs) || TREE_CODE (cs) == FUNCTION_DECL))
+ if (tree template_info = get_template_info (cs))
+ {
+ /* When parsing a function or class template, we in general need to
+ defer access checks until template instantiation time, since a friend
+ declaration may grant access only to a particular specialization of
+ the template. */
+
+ if (accessible_p (basetype_path, decl, /*consider_local_p=*/true))
+ /* But if the member is deemed accessible at parse time, then we can
+ assume it'll be accessible at instantiation time. */
+ return true;
+
+ /* Defer this access check until instantiation time. */
+ qualified_typedef_usage_t typedef_usage;
+ typedef_usage.typedef_decl = decl;
+ typedef_usage.context = TREE_TYPE (basetype_path);
+ typedef_usage.locus = input_location;
+ vec_safe_push (TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (template_info),
+ typedef_usage);
+ return true;
+ }
+
+ if (!accessible_p (basetype_path, decl, /*consider_local_p=*/true))
+ {
+ if (flag_new_inheriting_ctors)
+ diag_decl = strip_inheriting_ctors (diag_decl);
+ if (complain & tf_error)
+ complain_about_access (decl, diag_decl, true);
+ if (afi)
+ afi->record_access_failure (basetype_path, decl, diag_decl);
+ return false;
+ }
+
+ return true;
+}
+
/* Perform the access checks in CHECKS. The TREE_PURPOSE of each node
is the BINFO indicating the qualifying scope used to access the
DECL node stored in the TREE_VALUE of the node. If CHECKS is empty
deferred_access *ptr;
deferred_access_check *chk;
-
- /* Exit if we are in a context that no access checking is performed.
- */
+ /* Exit if we are in a context that no access checking is performed. */
if (deferred_access_no_check)
return true;
return ret;
}
-/* If we are currently parsing a template and we encountered a typedef
- TYPEDEF_DECL that is being accessed though CONTEXT, this function
- adds the typedef to a list tied to the current template.
- At template instantiation time, that list is walked and access check
- performed for each typedef.
- LOCATION is the location of the usage point of TYPEDEF_DECL. */
-
-void
-add_typedef_to_current_template_for_access_check (tree typedef_decl,
- tree context,
- location_t location)
-{
- tree template_info = NULL;
- tree cs = current_scope ();
-
- if (!is_typedef_decl (typedef_decl)
- || !context
- || !CLASS_TYPE_P (context)
- || !cs)
- return;
-
- if (CLASS_TYPE_P (cs) || TREE_CODE (cs) == FUNCTION_DECL)
- template_info = get_template_info (cs);
-
- if (template_info
- && TI_TEMPLATE (template_info)
- && !currently_open_class (context))
- append_type_to_template_for_access_check (cs, typedef_decl,
- context, location);
-}
-
/* DECL was the declaration to which a qualified-id resolved. Issue
an error message if it is not accessible. If OBJECT_TYPE is
non-NULL, we have just seen `x->' or `x.' and OBJECT_TYPE is the
tree nested_name_specifier,
tsubst_flags_t complain)
{
- tree scope;
- tree qualifying_type = NULL_TREE;
-
- /* If we are parsing a template declaration and if decl is a typedef,
- add it to a list tied to the template.
- At template instantiation time, that list will be walked and
- access check performed. */
- add_typedef_to_current_template_for_access_check (decl,
- nested_name_specifier
- ? nested_name_specifier
- : DECL_CONTEXT (decl),
- input_location);
-
/* If we're not checking, return immediately. */
if (deferred_access_no_check)
return true;
/* Determine the SCOPE of DECL. */
- scope = context_for_name_lookup (decl);
+ tree scope = context_for_name_lookup (decl);
/* If the SCOPE is not a type, then DECL is not a member. */
- if (!TYPE_P (scope))
+ if (!TYPE_P (scope)
+ /* If SCOPE is dependent then we can't perform this access check now,
+ and since we'll perform this access check again after substitution
+ there's no need to explicitly defer it. */
+ || dependent_type_p (scope))
return true;
+
+ tree qualifying_type = NULL_TREE;
/* Compute the scope through which DECL is being accessed. */
if (object_type
/* OBJECT_TYPE might not be a class type; consider:
if (qualifying_type
/* It is possible for qualifying type to be a TEMPLATE_TYPE_PARM
or similar in a default argument value. */
- && CLASS_TYPE_P (qualifying_type)
- && !dependent_type_p (qualifying_type))
+ && CLASS_TYPE_P (qualifying_type))
return perform_or_defer_access_check (TYPE_BINFO (qualifying_type), decl,
decl, complain);
struct b;
template <typename b> struct f { using e = b; };
template <typename ai> struct m { typedef g<ai> aj; };
-template <typename b> class n { typedef typename m<b>::aj e; };
+template <typename b> struct n { typedef typename m<b>::aj e; };
template <typename b> using an = typename n<b>::e;
template <typename> constexpr bool ao = c<true>::d;
template <typename> constexpr bool i = c<1>::d;
struct {
template <de da, typename b> void operator()(da, b);
} di;
-class p {
+struct p {
void begin();
};
template <typename> using df = p;
{
using::mbstate_t;
typedef int *__c_locale;
- class locale
+ struct locale
{
class facet;
};
typedef basic_ostream<char> ostream;
template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
class num_get;
- class locale {
+ struct locale {
class facet;
};
class locale::facet {
{
class locale
{
+public:
class facet;
+private:
class _Impl;
_Impl *_M_impl;
};
int _M_word_size;
_Words *_M_word;
locale _M_ios_locale;
+protected:
virtual ~ ios_base ();
};
template < typename, typename > class istreambuf_iterator
;
template<typename _Signature>
class function;
- class _Function_base
+ struct _Function_base
{
template<typename _Functor>
class _Base_manager
struct B {
typedef __SIZE_TYPE__ H;
};
-template <typename> class allocator : B {};
+template <typename> class allocator : public B {};
template <typename _Alloc> struct C {
template <typename T>
static typename T::H foo(T *);
--- /dev/null
+// PR c++/41437
+// { dg-do compile }
+
+class A { struct B { B(); }; };
+template<typename T> void f() { A::B b; } // { dg-error "private" }
+void g() { f<int>(); }
+
+class X { template<typename> struct A{}; };
+
+X::A<int> a; // { dg-error "private" }
--- /dev/null
+// PR c++/47346
+// { dg-do compile }
+
+class C
+{
+ struct Private { };
+};
+
+template<typename T>
+struct exploit1
+{
+ typedef C::Private type; // { dg-error "private" }
+};
+exploit1<int>::type x1;
+
+template<typename T>
+struct exploit2 : C::Private // { dg-error "private" }
+{
+};
+exploit2<int> x2;
+
+template<typename T>
+struct exploit3
+{
+ template<class U = C::Private> // { dg-error "private" }
+ struct E {};
+};
+
+exploit3<int>::E<> e;
template<typename _Tp> struct rebind { typedef typename _Base_type::template rebind_alloc<_Tp> other; };
};
-template<typename _Tp> class allocator {
+template<typename _Tp> struct allocator {
typedef _Tp value_type;
template<typename _Tp1> struct rebind { typedef allocator<_Tp1> other; };
};
--- /dev/null
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <type_traits>
+
+class Class { Class() {} };
+
+template <typename X> static bool foo() {
+ return std::is_constructible<Class>::value;
+}
+
+static_assert(!std::is_constructible<Class>::value, "");