From: Jason Merrill <jason@redhat.com>
To: Marek Polacek <polacek@redhat.com>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>,
libstdc++ <libstdc++@gcc.gnu.org>,
Jonathan Wakely <jwakely@redhat.com>
Subject: Re: [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477]
Date: Thu, 14 Jul 2022 23:48:51 -0400 [thread overview]
Message-ID: <93e09683-22c9-9e13-9ae1-13c4b447f38f@redhat.com> (raw)
In-Reply-To: <YtBV1TwNVSgJIddf@redhat.com>
On 7/14/22 13:43, Marek Polacek wrote:
> On Tue, Jul 12, 2022 at 04:15:00PM -0400, Jason Merrill wrote:
>> On 7/12/22 16:10, Jason Merrill wrote:
>>> On 7/8/22 13:41, Marek Polacek wrote:
>>>> This patch implements C++23 P2255R2, which adds two new type traits to
>>>> detect reference binding to a temporary. They can be used to detect code
>>>> like
>>>>
>>>> std::tuple<const std::string&> t("meow");
>>>>
>>>> which is incorrect because it always creates a dangling reference,
>>>> because
>>>> the std::string temporary is created inside the selected constructor of
>>>> std::tuple, and not outside it.
>>>>
>>>> There are two new compiler builtins,
>>>> __reference_constructs_from_temporary
>>>> and __reference_converts_from_temporary. The former is used to simulate
>>>> direct- and the latter copy-initialization context. But I had a
>>>> hard time
>>>> finding a test where there's actually a difference. Under DR 2267, both
>>>> of these are invalid:
>>>>
>>>> struct A { } a;
>>>> struct B { explicit B(const A&); };
>>>> const B &b1{a};
>>>> const B &b2(a);
>>>>
>>>> so I had to peruse [over.match.ref], and eventually realized that the
>>>> difference can be seen here:
>>>>
>>>> struct G {
>>>> operator int(); // #1
>>>> explicit operator int&&(); // #2
>>>> };
>>>>
>>>> int&& r1(G{}); // use #2 (no temporary)
>>>> int&& r2 = G{}; // use #1 (a temporary is created to be bound to int&&)
>>>>
>>>> The implementation itself was rather straightforward because we already
>>>> have conv_binds_ref_to_prvalue. The main function here is
>>>> reference_from_temporary. The renaming to ref_conv_binds_to_temporary_p
>>>> is because previously the function didn't distinguish between an invalid
>>>> conversion and one that binds to a prvalue.
>>>>
>>>> The patch also adds the relevant class and variable templates to
>>>> <type_traits>.
>>>>
>>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>>>>
>>>> PR c++/104477
>>>>
>>>> gcc/c-family/ChangeLog:
>>>>
>>>> * c-common.cc (c_common_reswords): Add
>>>> __reference_constructs_from_temporary and
>>>> __reference_converts_from_temporary.
>>>> * c-common.h (enum rid): Add RID_REF_CONSTRUCTS_FROM_TEMPORARY and
>>>> RID_REF_CONVERTS_FROM_TEMPORARY.
>>>>
>>>> gcc/cp/ChangeLog:
>>>>
>>>> * call.cc (ref_conv_binds_directly_p): Rename to ...
>>>> (ref_conv_binds_to_temporary_p): ... this. Add a new bool
>>>> parameter. Return true only if the conversion is valid and
>>>> conv_binds_ref_to_prvalue returns true.
>>>> * constraint.cc (diagnose_trait_expr): Handle
>>>> CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
>>>> CPTK_REF_CONVERTS_FROM_TEMPORARY.
>>>> * cp-tree.h (enum cp_trait_kind): Add
>>>> CPTK_REF_CONSTRUCTS_FROM_TEMPORARY
>>>> and CPTK_REF_CONVERTS_FROM_TEMPORARY.
>>>> (ref_conv_binds_directly_p): Rename to ...
>>>> (ref_conv_binds_to_temporary_p): ... this.
>>>> (reference_from_temporary): Declare.
>>>> * cxx-pretty-print.cc (pp_cxx_trait_expression): Handle
>>>> CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
>>>> CPTK_REF_CONVERTS_FROM_TEMPORARY.
>>>> * method.cc (reference_from_temporary): New.
>>>> * parser.cc (cp_parser_primary_expression): Handle
>>>> RID_REF_CONSTRUCTS_FROM_TEMPORARY and
>>>> RID_REF_CONVERTS_FROM_TEMPORARY.
>>>> (cp_parser_trait_expr): Likewise.
>>>> (warn_for_range_copy): Adjust to call ref_conv_binds_to_temporary_p.
>>>> * semantics.cc (trait_expr_value): Handle
>>>> CPTK_REF_CONSTRUCTS_FROM_TEMPORARY and
>>>> CPTK_REF_CONVERTS_FROM_TEMPORARY.
>>>> (finish_trait_expr): Likewise.
>>>>
>>>> libstdc++-v3/ChangeLog:
>>>>
>>>> * include/std/type_traits (reference_constructs_from_temporary,
>>>> reference_converts_from_temporary): New class templates.
>>>> (reference_constructs_from_temporary_v,
>>>> reference_converts_from_temporary_v): New variable templates.
>>>> (__cpp_lib_reference_from_temporary): Define for C++23.
>>>> * include/std/version (__cpp_lib_reference_from_temporary):
>>>> Define for
>>>> C++23.
>>>> * testsuite/20_util/variable_templates_for_traits.cc: Test
>>>> reference_constructs_from_temporary_v and
>>>> reference_converts_from_temporary_v.
>>>> * testsuite/20_util/reference_from_temporary/value.cc: New test.
>>>> * testsuite/20_util/reference_from_temporary/value2.cc: New test.
>>>> * testsuite/20_util/reference_from_temporary/version.cc: New test.
>>>>
>>>> gcc/testsuite/ChangeLog:
>>>>
>>>> * g++.dg/ext/reference_constructs_from_temporary1.C: New test.
>>>> * g++.dg/ext/reference_converts_from_temporary1.C: New test.
>>>> ---
>>>> gcc/c-family/c-common.cc | 4 +
>>>> gcc/c-family/c-common.h | 2 +
>>>> gcc/cp/call.cc | 14 +-
>>>> gcc/cp/constraint.cc | 8 +
>>>> gcc/cp/cp-tree.h | 7 +-
>>>> gcc/cp/cxx-pretty-print.cc | 6 +
>>>> gcc/cp/method.cc | 28 +++
>>>> gcc/cp/parser.cc | 14 +-
>>>> gcc/cp/semantics.cc | 8 +
>>>> .../reference_constructs_from_temporary1.C | 214 ++++++++++++++++++
>>>> .../ext/reference_converts_from_temporary1.C | 214 ++++++++++++++++++
>>>> libstdc++-v3/include/std/type_traits | 39 ++++
>>>> libstdc++-v3/include/std/version | 5 +-
>>>> .../20_util/reference_from_temporary/value.cc | 110 +++++++++
>>>> .../reference_from_temporary/value2.cc | 28 +++
>>>> .../reference_from_temporary/version.cc | 27 +++
>>>> .../20_util/variable_templates_for_traits.cc | 14 ++
>>>> 17 files changed, 730 insertions(+), 12 deletions(-)
>>>> create mode 100644
>>>> gcc/testsuite/g++.dg/ext/reference_constructs_from_temporary1.C
>>>> create mode 100644
>>>> gcc/testsuite/g++.dg/ext/reference_converts_from_temporary1.C
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/20_util/reference_from_temporary/value.cc
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/20_util/reference_from_temporary/value2.cc
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/20_util/reference_from_temporary/version.cc
>>>>
>>>> diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
>>>> index 1b8e73f7bc5..655c3aefee6 100644
>>>> --- a/gcc/c-family/c-common.cc
>>>> +++ b/gcc/c-family/c-common.cc
>>>> @@ -537,6 +537,10 @@ const struct c_common_resword c_common_reswords[] =
>>>> { "__is_constructible", RID_IS_CONSTRUCTIBLE, D_CXXONLY },
>>>> { "__is_nothrow_assignable", RID_IS_NOTHROW_ASSIGNABLE, D_CXXONLY },
>>>> { "__is_nothrow_constructible", RID_IS_NOTHROW_CONSTRUCTIBLE,
>>>> D_CXXONLY },
>>>> + { "__reference_constructs_from_temporary",
>>>> RID_REF_CONSTRUCTS_FROM_TEMPORARY,
>>>> + D_CXXONLY },
>>>> + { "__reference_converts_from_temporary",
>>>> RID_REF_CONVERTS_FROM_TEMPORARY,
>>>> + D_CXXONLY },
>>>> /* C++ transactional memory. */
>>>> { "synchronized", RID_SYNCHRONIZED, D_CXX_OBJC | D_TRANSMEM },
>>>> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
>>>> index c0900848965..f9064393b4e 100644
>>>> --- a/gcc/c-family/c-common.h
>>>> +++ b/gcc/c-family/c-common.h
>>>> @@ -184,6 +184,8 @@ enum rid
>>>> RID_IS_UNION, RID_UNDERLYING_TYPE,
>>>> RID_IS_ASSIGNABLE, RID_IS_CONSTRUCTIBLE,
>>>> RID_IS_NOTHROW_ASSIGNABLE, RID_IS_NOTHROW_CONSTRUCTIBLE,
>>>> + RID_REF_CONSTRUCTS_FROM_TEMPORARY,
>>>> + RID_REF_CONVERTS_FROM_TEMPORARY,
>>>> /* C++11 */
>>>> RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR,
>>>> RID_STATIC_ASSERT,
>>>> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
>>>> index fc98552fda2..1ba209f61f1 100644
>>>> --- a/gcc/cp/call.cc
>>>> +++ b/gcc/cp/call.cc
>>>> @@ -9109,21 +9109,23 @@ conv_binds_ref_to_prvalue (conversion *c)
>>>> return conv_is_prvalue (next_conversion (c));
>>>> }
>>>> -/* True iff converting EXPR to a reference type TYPE does not involve
>>>> - creating a temporary. */
>>>> +/* True iff converting EXPR to a reference type TYPE binds the
>>>> reference to
>>>> + a temporary. DIRECT_INIT_P says whether the conversion should
>>>> be done
>>>> + in direct- or copy-initialization context. */
>>>> bool
>>>> -ref_conv_binds_directly_p (tree type, tree expr)
>>>> +ref_conv_binds_to_temporary_p (tree type, tree expr,
>>>> + bool direct_init_p /*= false*/)
>>>> {
>>>> gcc_assert (TYPE_REF_P (type));
>>>> /* Get the high-water mark for the CONVERSION_OBSTACK. */
>>>> void *p = conversion_obstack_alloc (0);
>>>> + const int flags = direct_init_p ? LOOKUP_NORMAL : LOOKUP_IMPLICIT;
>>>> conversion *conv = implicit_conversion (type, TREE_TYPE (expr), expr,
>>>> - /*c_cast_p=*/false,
>>>> - LOOKUP_IMPLICIT, tf_none);
>>>> - bool ret = conv && !conv->bad_p && !conv_binds_ref_to_prvalue (conv);
>>>> + /*c_cast_p=*/false, flags, tf_none);
>>>> + bool ret = conv && !conv->bad_p && conv_binds_ref_to_prvalue (conv);
>>>> /* Free all the conversions we allocated. */
>>>> obstack_free (&conversion_obstack, p);
>>>> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
>>>> index 591155cee22..648cc9d176d 100644
>>>> --- a/gcc/cp/constraint.cc
>>>> +++ b/gcc/cp/constraint.cc
>>>> @@ -3687,6 +3687,14 @@ diagnose_trait_expr (tree expr, tree args)
>>>> case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
>>>> inform (loc, " %qT does not have unique object
>>>> representations", t1);
>>>> break;
>>>> + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>>>> + inform (loc, " %qT is not a reference that binds to a temporary "
>>>> + "object of type %qT (direct-initialization)", t1, t2);
>>>> + break;
>>>> + case CPTK_REF_CONVERTS_FROM_TEMPORARY:
>>>> + inform (loc, " %qT is not a reference that binds to a temporary "
>>>> + "object of type %qT (copy-initialization)", t1, t2);
>>>> + break;
>>>> case CPTK_BASES:
>>>> case CPTK_DIRECT_BASES:
>>>> case CPTK_UNDERLYING_TYPE:
>>>> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
>>>> index 2fde4f83b41..c3bed31e455 100644
>>>> --- a/gcc/cp/cp-tree.h
>>>> +++ b/gcc/cp/cp-tree.h
>>>> @@ -1397,7 +1397,9 @@ enum cp_trait_kind
>>>> CPTK_IS_ASSIGNABLE,
>>>> CPTK_IS_CONSTRUCTIBLE,
>>>> CPTK_IS_NOTHROW_ASSIGNABLE,
>>>> - CPTK_IS_NOTHROW_CONSTRUCTIBLE
>>>> + CPTK_IS_NOTHROW_CONSTRUCTIBLE,
>>>> + CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
>>>> + CPTK_REF_CONVERTS_FROM_TEMPORARY
>>>> };
>>>> /* The types that we are processing. */
>>>> @@ -6520,7 +6522,7 @@ extern bool sufficient_parms_p
>>>> (const_tree);
>>>> extern tree type_decays_to (tree);
>>>> extern tree extract_call_expr (tree);
>>>> extern tree build_trivial_dtor_call (tree, bool = false);
>>>> -extern bool ref_conv_binds_directly_p (tree, tree);
>>>> +extern bool ref_conv_binds_to_temporary_p (tree, tree, bool = false);
>>>> extern tree build_user_type_conversion (tree, tree, int,
>>>> tsubst_flags_t);
>>>> extern tree build_new_function_call (tree, vec<tree, va_gc> **,
>>>> @@ -7105,6 +7107,7 @@ extern tree forward_parm (tree);
>>>> extern bool is_trivially_xible (enum tree_code, tree, tree);
>>>> extern bool is_nothrow_xible (enum tree_code, tree, tree);
>>>> extern bool is_xible (enum tree_code, tree, tree);
>>>> +extern bool reference_from_temporary (tree, tree, bool);
>>>> extern tree get_defaulted_eh_spec (tree, tsubst_flags_t =
>>>> tf_warning_or_error);
>>>> extern bool maybe_explain_implicit_delete (tree);
>>>> extern void explain_implicit_non_constexpr (tree);
>>>> diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
>>>> index 7e4db2e413b..44590830a61 100644
>>>> --- a/gcc/cp/cxx-pretty-print.cc
>>>> +++ b/gcc/cp/cxx-pretty-print.cc
>>>> @@ -2696,6 +2696,12 @@ pp_cxx_trait_expression (cxx_pretty_printer
>>>> *pp, tree t)
>>>> case CPTK_IS_NOTHROW_CONSTRUCTIBLE:
>>>> pp_cxx_ws_string (pp, "__is_nothrow_constructible");
>>>> break;
>>>> + case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY:
>>>> + pp_cxx_ws_string (pp, "__reference_constructs_from_temporary");
>>>> + break;
>>>> + case CPTK_REF_CONVERTS_FROM_TEMPORARY:
>>>> + pp_cxx_ws_string (pp, "__reference_converts_from_temporary");
>>>> + break;
>>>> default:
>>>> gcc_unreachable ();
>>>> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
>>>> index 0dffd648b0b..dd9715b6725 100644
>>>> --- a/gcc/cp/method.cc
>>>> +++ b/gcc/cp/method.cc
>>>> @@ -2211,6 +2211,34 @@ is_xible (enum tree_code code, tree to, tree from)
>>>> return !!expr;
>>>> }
>>>> +/* Return true iff conjunction_v<is_reference<T>,
>>>> is_constructible<T, U>> is
>>>> + true, and the initialization
>>>> + T t(VAL<U>); // DIRECT_INIT_P
>>>> + or
>>>> + T t = VAL<U>; // !DIRECT_INIT_P
>>>> + binds t to a temporary object whose lifetime is extended.
>>>> + VAL<T> is defined in [meta.unary.prop]:
>>>> + -- If T is a reference or function type, VAL<T> is an expression
>>>> with the
>>>> + same type and value category as declval<T>().
>>>> + -- Otherwise, VAL<T> is a prvalue that initially has type T. */
>>>> +
>>>> +bool
>>>> +reference_from_temporary (tree to, tree from, bool direct_init_p)
>>>> +{
>>>> + /* Check is_reference<T>. */
>>>> + if (!TYPE_REF_P (to))
>>>> + return false;
>>>> + /* Check is_constructible<T, U>.
>>>> + ??? This check doesn't seem to be necessary; if T isn't
>>>> constructible
>>>> + from U, we won't be able to create a conversion. */
>>>> + if (!is_xible (INIT_EXPR, to, build_tree_list (NULL_TREE, from)))
>>>> + return false;
>>>
>>> I agree with the comment, did you try leaving this out? If it stays I'd
>>> think it needs to consider direct_init_p.
>>>
>>>> + tree val = build_stub_object (from);
>>>> + if (!TYPE_REF_P (from) && TREE_CODE (from) != FUNCTION_TYPE)
>>>> + val = CLASS_TYPE_P (from) ? force_rvalue (val, tf_none) :
>>>> rvalue (val);
>>>> + return ref_conv_binds_to_temporary_p (to, val, direct_init_p);
>>>> +}
>>>> +
>>>> /* Categorize various special_function_kinds. */
>>>> #define SFK_CTOR_P(sfk) \
>>>> ((sfk) >= sfk_constructor && (sfk) <= sfk_move_constructor)
>>>> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
>>>> index bf9ea3685f8..edee94bda13 100644
>>>> --- a/gcc/cp/parser.cc
>>>> +++ b/gcc/cp/parser.cc
>>>> @@ -5917,6 +5917,8 @@ cp_parser_primary_expression (cp_parser *parser,
>>>> case RID_IS_CONSTRUCTIBLE:
>>>> case RID_IS_NOTHROW_ASSIGNABLE:
>>>> case RID_IS_NOTHROW_CONSTRUCTIBLE:
>>>> + case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
>>>> + case RID_REF_CONVERTS_FROM_TEMPORARY:
>>>> return cp_parser_trait_expr (parser, token->keyword);
>>>> // C++ concepts
>>>> @@ -10988,6 +10990,14 @@ cp_parser_trait_expr (cp_parser* parser,
>>>> enum rid keyword)
>>>> kind = CPTK_IS_NOTHROW_CONSTRUCTIBLE;
>>>> variadic = true;
>>>> break;
>>>> + case RID_REF_CONSTRUCTS_FROM_TEMPORARY:
>>>> + kind = CPTK_REF_CONSTRUCTS_FROM_TEMPORARY;
>>>> + binary = true;
>>>> + break;
>>>> + case RID_REF_CONVERTS_FROM_TEMPORARY:
>>>> + kind = CPTK_REF_CONVERTS_FROM_TEMPORARY;
>>>> + binary = true;
>>>> + break;
>>>> default:
>>>> gcc_unreachable ();
>>>> }
>>>> @@ -13811,7 +13821,7 @@ warn_for_range_copy (tree decl, tree expr)
>>>> if (TYPE_REF_P (type))
>>>> {
>>>> - if (glvalue_p (expr) && !ref_conv_binds_directly_p (type, expr))
>>>> + if (glvalue_p (expr) && ref_conv_binds_to_temporary_p (type,
>>>> expr))
>>>> {
>>>> auto_diagnostic_group d;
>>>> if (warning_at (loc, OPT_Wrange_loop_construct,
>>>> @@ -13842,7 +13852,7 @@ warn_for_range_copy (tree decl, tree expr)
>>>> tree rtype = cp_build_reference_type (type, /*rval*/false);
>>>> /* If we could initialize the reference directly, it wouldn't
>>>> involve any
>>>> copies. */
>>>> - if (!ref_conv_binds_directly_p (rtype, expr))
>>>> + if (ref_conv_binds_to_temporary_p (rtype, expr))
>>>> return;
>>>
>>> I think this case wants the old handling of invalid conversions you
>>> mentioned in your intro; we don't want to suggest changing to a
>>> reference if that's ill-formed.
>>>
>>> In passing we might change the comment to "If we can initialize a
>>> reference directly, suggest that to avoid the copy." and move it above
>>> the rtype declaration.
>>
>> Hmm, and I suspect we get false positives when expr is a prvalue, so
>> initializing a non-reference variable of the same cv-unqualified type
>> involves no extra copy?
>
> I couldn't provoke a false positive here. Note that ref_conv_binds_to_temporary_p
> always gets a reference so copy elision isn't applicable here.
Ah, I see: if expr is a prvalue, that counts as binding a reference to a
temporary, so we (properly) don't think the reference is an improvement.
The function name reference_from_temporary seems to me to suggest a tree
value rather than bool, maybe rename to ref_xes_from_temporary?
OK either way.
Jason
next prev parent reply other threads:[~2022-07-15 3:49 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-08 17:41 [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] Marek Polacek
2022-07-11 10:19 ` Jonathan Wakely
2022-07-12 17:16 ` [PATCH v2] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] Marek Polacek
2022-07-12 20:10 ` [PATCH] " Jason Merrill
2022-07-12 20:15 ` Jason Merrill
2022-07-14 17:43 ` Marek Polacek
2022-07-15 3:48 ` Jason Merrill [this message]
2022-07-15 15:32 ` Marek Polacek
2022-07-15 19:59 ` [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] Ville Voutilainen
2022-07-15 20:25 ` [PATCH] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] Marek Polacek
2022-07-17 21:12 ` Stephan Bergmann
2022-07-19 10:08 ` [PATCH] c++: Add __reference_con{struc, ver}ts_from_temporary [PR104477] Jonathan Wakely
2022-07-19 11:10 ` Jonathan Wakely
2022-07-19 14:14 ` Jonathan Wakely
2022-07-14 17:41 ` [PATCH v3] c++: Add __reference_con{struc,ver}ts_from_temporary [PR104477] Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=93e09683-22c9-9e13-9ae1-13c4b447f38f@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
--cc=polacek@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).