Skip to content

Commit 99c2ed7

Browse files
committed
c++/modules: Allow ignoring some TU-local exposure errors in GMF [PR121574]
A frequent issue with migrating to C++20 modules has been dealing with third-party libraries with internal functions or data. This causes GCC to currently refuse to build the module if any references to these internal-linkage declarations escape into the module CMI. This can seem needlessly hostile, however, especially since we have the capabilities to support this (to a degree) from header units, albeit with the inherent ODR issues associated with their use. In aid of this, this patch demotes the error to a pedwarn in various scenarios, by treating some declarations as not being TU-local even if they otherwise would have been. Effort has been made to not alter semantics of valid programs, and to continue to diagnose cases that the standard says we must. In particular, any code in the module purview is still a hard error, due to the inherent issues with exposing TU-local entities, and the lack of any migration requirements. Because this patch is just to assist migration, we only deal with the simplest (yet most common) cases: namespace scope functions and variables. Types are hard to handle neatly as we risk getting thousands of unhelpful warnings as we continue to walk the type body and find new TU-local entities to complain about. Templates are also tricky because it's hard to tell if an instantiation that occurred in the module purview only refers to global module entities or if it's inadvertantly exposing a purview entity as well. Neither of these are likely to occur frequently in third-party code; if need be, this can be relaxed later as well. Similarly, even in the GMF a constexpr variable with a TU-local value will not be usable in constant expressions in the importer, and since we cannot easily warn about this from the importer we continue to make this an error in the module interface. PR c++/121574 gcc/c-family/ChangeLog: * c.opt: New warning '-Wexpose-global-module-tu-local'. * c.opt.urls: Regenerate. gcc/ChangeLog: * doc/invoke.texi: Document '-Wexpose-global-module-tu-local'. gcc/cp/ChangeLog: * module.cc (depset::disc_bits): Replace 'DB_REFS_TU_LOCAL_BIT' and 'DB_EXPOSURE_BIT' with new four flags 'DB_{REF,EXPOSE}_{GLOBAL,PURVIEW}_BIT'. (depset::is_tu_local): Support checking either for only purview TU-local entities or any entity described TU-local by standard. (depset::refs_tu_local): Likewise. (depset::is_exposure): Likewise. (depset::hash::make_dependency): A constant initialized to a TU-local variable is always considered a purview exposure. (is_exposure_of_member_type): Adjust sanity checks to handle if we ever relax requirements for TU-local types. (depset::hash::add_dependency): Differentiate referencing purview or GMF TU-local entities. (depset::hash::diagnose_bad_internal_ref): New function. (depset::hash::diagnose_template_names_tu_local): New function. (depset::hash::finalize_dependencies): Handle new warnings that might be needed for GMF TU-local entities. gcc/testsuite/ChangeLog: * g++.dg/modules/internal-17_a.C: New test. * g++.dg/modules/internal-17_b.C: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> Reviewed-by: Jason Merrill <jason@redhat.com>
1 parent 35e0295 commit 99c2ed7

File tree

6 files changed

+372
-71
lines changed

6 files changed

+372
-71
lines changed

gcc/c-family/c.opt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,10 @@ Wexternal-tu-local
765765
C++ ObjC++ Var(warn_tu_local) Warning Init(1)
766766
Warn about naming a TU-local entity declared in another translation unit.
767767

768+
Wexpose-global-module-tu-local
769+
C++ ObjC++ Var(warn_expose_global_module_tu_local) Init(1) Warning
770+
Warn when a module exposes a TU-local entity from the global module fragment.
771+
768772
Wextra
769773
C ObjC C++ ObjC++ Warning
770774
; in common.opt

gcc/c-family/c.opt.urls

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ UrlSuffix(gcc/Warning-Options.html#index-Wexpansion-to-defined)
379379
Wexternal-tu-local
380380
UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexternal-tu-local)
381381

382+
Wexpose-global-module-tu-local
383+
UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wexpose-global-module-tu-local)
384+
382385
Wextra
383386
UrlSuffix(gcc/Warning-Options.html#index-Wextra) LangUrlSuffix_D(gdc/Warnings.html#index-Wextra) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Wextra)
384387

gcc/cp/module.cc

Lines changed: 217 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,10 +2365,11 @@ class depset {
23652365
DB_KIND_BITS = EK_BITS,
23662366
DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
23672367
DB_IS_PENDING_BIT, /* Is a maybe-pending entity. */
2368-
DB_TU_LOCAL_BIT, /* It is a TU-local entity. */
2369-
DB_REFS_TU_LOCAL_BIT, /* Refers to a TU-local entity (but is not
2370-
necessarily an exposure.) */
2371-
DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */
2368+
DB_TU_LOCAL_BIT, /* Is a TU-local entity. */
2369+
DB_REF_GLOBAL_BIT, /* Refers to a GMF TU-local entity. */
2370+
DB_REF_PURVIEW_BIT, /* Refers to a purview TU-local entity. */
2371+
DB_EXPOSE_GLOBAL_BIT, /* Exposes a GMF TU-local entity. */
2372+
DB_EXPOSE_PURVIEW_BIT, /* Exposes a purview TU-local entity. */
23722373
DB_IMPORTED_BIT, /* An imported entity. */
23732374
DB_UNREACHED_BIT, /* A yet-to-be reached entity. */
23742375
DB_MAYBE_RECURSIVE_BIT, /* An entity maybe in a recursive cluster. */
@@ -2458,19 +2459,40 @@ class depset {
24582459
|| (get_entity_kind () == EK_DECL
24592460
&& get_flag_bit<DB_IS_PENDING_BIT> ()));
24602461
}
2462+
24612463
public:
2462-
bool is_tu_local () const
2464+
/* Only consider global module entities as being TU-local
2465+
when STRICT is set; otherwise, as an extension we support
2466+
emitting declarations referencing TU-local GMF entities
2467+
(and only check purview entities), to assist in migration. */
2468+
bool is_tu_local (bool strict = false) const
24632469
{
2464-
return get_flag_bit<DB_TU_LOCAL_BIT> ();
2470+
/* Non-strict is only intended for migration purposes, so
2471+
for simplicity's sake we only care about whether this is
2472+
a non-purview variable or function at namespace scope;
2473+
these are the most common cases (coming from C), and
2474+
that way we don't have to care about diagnostics for
2475+
nested types and so forth. */
2476+
tree inner = STRIP_TEMPLATE (get_entity ());
2477+
return (get_flag_bit<DB_TU_LOCAL_BIT> ()
2478+
&& (strict
2479+
|| !VAR_OR_FUNCTION_DECL_P (inner)
2480+
|| !NAMESPACE_SCOPE_P (inner)
2481+
|| (DECL_LANG_SPECIFIC (inner)
2482+
&& DECL_MODULE_PURVIEW_P (inner))));
24652483
}
2466-
bool refs_tu_local () const
2484+
bool refs_tu_local (bool strict = false) const
24672485
{
2468-
return get_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
2486+
return (get_flag_bit<DB_REF_PURVIEW_BIT> ()
2487+
|| (strict && get_flag_bit <DB_REF_GLOBAL_BIT> ()));
24692488
}
2470-
bool is_exposure () const
2489+
bool is_exposure (bool strict = false) const
24712490
{
2472-
return get_flag_bit<DB_EXPOSURE_BIT> ();
2491+
return (get_flag_bit<DB_EXPOSE_PURVIEW_BIT> ()
2492+
|| (strict && get_flag_bit <DB_EXPOSE_GLOBAL_BIT> ()));
24732493
}
2494+
2495+
public:
24742496
bool is_import () const
24752497
{
24762498
return get_flag_bit<DB_IMPORTED_BIT> ();
@@ -2662,6 +2684,10 @@ class depset {
26622684
void find_dependencies (module_state *);
26632685
bool finalize_dependencies ();
26642686
vec<depset *> connect ();
2687+
2688+
private:
2689+
bool diagnose_bad_internal_ref (depset *dep, bool strict = false);
2690+
bool diagnose_template_names_tu_local (depset *dep, bool strict = false);
26652691
};
26662692

26672693
public:
@@ -14335,8 +14361,14 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
1433514361
if (DECL_DECLARED_CONSTEXPR_P (decl)
1433614362
|| DECL_INLINE_VAR_P (decl))
1433714363
/* A constexpr variable initialized to a TU-local value,
14338-
or an inline value (PR c++/119996), is an exposure. */
14339-
dep->set_flag_bit<DB_EXPOSURE_BIT> ();
14364+
or an inline value (PR c++/119996), is an exposure.
14365+
14366+
For simplicity, we don't support "non-strict" TU-local
14367+
values: even if the TU-local entity we refer to in the
14368+
initialiser is in the GMF, we still won't consider this
14369+
valid in constant expressions in other TUs, and so
14370+
complain accordingly. */
14371+
dep->set_flag_bit<DB_EXPOSE_PURVIEW_BIT> ();
1434014372
}
1434114373
}
1434214374

@@ -14426,11 +14458,13 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
1442614458
static bool
1442714459
is_exposure_of_member_type (depset *source, depset *ref)
1442814460
{
14429-
gcc_checking_assert (source->refs_tu_local () && ref->is_tu_local ());
14461+
gcc_checking_assert (source->refs_tu_local (/*strict=*/true)
14462+
&& ref->is_tu_local (/*strict=*/true));
1443014463
tree source_entity = STRIP_TEMPLATE (source->get_entity ());
1443114464
tree ref_entity = STRIP_TEMPLATE (ref->get_entity ());
1443214465

14433-
if (source_entity
14466+
if (!source->is_tu_local (/*strict=*/true)
14467+
&& source_entity
1443414468
&& ref_entity
1443514469
&& DECL_IMPLICIT_TYPEDEF_P (source_entity)
1443614470
&& DECL_IMPLICIT_TYPEDEF_P (ref_entity)
@@ -14453,11 +14487,20 @@ depset::hash::add_dependency (depset *dep)
1445314487
gcc_checking_assert (current && !is_key_order ());
1445414488
current->deps.safe_push (dep);
1445514489

14456-
if (dep->is_tu_local ())
14490+
if (dep->is_tu_local (/*strict=*/true))
1445714491
{
14458-
current->set_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
14492+
if (dep->is_tu_local ())
14493+
current->set_flag_bit<DB_REF_PURVIEW_BIT> ();
14494+
else
14495+
current->set_flag_bit<DB_REF_GLOBAL_BIT> ();
14496+
1445914497
if (!ignore_tu_local && !is_exposure_of_member_type (current, dep))
14460-
current->set_flag_bit<DB_EXPOSURE_BIT> ();
14498+
{
14499+
if (dep->is_tu_local ())
14500+
current->set_flag_bit<DB_EXPOSE_PURVIEW_BIT> ();
14501+
else
14502+
current->set_flag_bit<DB_EXPOSE_GLOBAL_BIT> ();
14503+
}
1446114504
}
1446214505

1446314506
if (current->get_entity_kind () == EK_USING
@@ -15342,6 +15385,128 @@ template_has_explicit_inst (tree tmpl)
1534215385
return false;
1534315386
}
1534415387

15388+
/* Complain about DEP that exposes a TU-local entity.
15389+
15390+
If STRICT, DEP only referenced entities from the GMF. Returns TRUE
15391+
if we explained anything. */
15392+
15393+
bool
15394+
depset::hash::diagnose_bad_internal_ref (depset *dep, bool strict)
15395+
{
15396+
tree decl = dep->get_entity ();
15397+
15398+
/* Don't need to walk if we're not going to be emitting
15399+
any diagnostics anyway. */
15400+
if (strict && !warning_enabled_at (DECL_SOURCE_LOCATION (decl),
15401+
OPT_Wexpose_global_module_tu_local))
15402+
return false;
15403+
15404+
for (depset *rdep : dep->deps)
15405+
if (!rdep->is_binding () && rdep->is_tu_local (strict)
15406+
&& !is_exposure_of_member_type (dep, rdep))
15407+
{
15408+
// FIXME:QOI Better location information? We're
15409+
// losing, so it doesn't matter about efficiency.
15410+
tree exposed = rdep->get_entity ();
15411+
auto_diagnostic_group d;
15412+
if (strict)
15413+
{
15414+
/* Allow suppressing the warning from the point of declaration
15415+
of the otherwise-exposed decl, for cases we know that
15416+
exposures will never be 'bad'. */
15417+
if (warning_enabled_at (DECL_SOURCE_LOCATION (exposed),
15418+
OPT_Wexpose_global_module_tu_local)
15419+
&& pedwarn (DECL_SOURCE_LOCATION (decl),
15420+
OPT_Wexpose_global_module_tu_local,
15421+
"%qD exposes TU-local entity %qD", decl, exposed))
15422+
{
15423+
bool informed = is_tu_local_entity (exposed, /*explain=*/true);
15424+
gcc_checking_assert (informed);
15425+
return true;
15426+
}
15427+
}
15428+
else
15429+
{
15430+
error_at (DECL_SOURCE_LOCATION (decl),
15431+
"%qD exposes TU-local entity %qD", decl, exposed);
15432+
bool informed = is_tu_local_entity (exposed, /*explain=*/true);
15433+
gcc_checking_assert (informed);
15434+
if (dep->is_tu_local (/*strict=*/true))
15435+
inform (DECL_SOURCE_LOCATION (decl),
15436+
"%qD is also TU-local but has been exposed elsewhere",
15437+
decl);
15438+
return true;
15439+
}
15440+
}
15441+
15442+
return false;
15443+
}
15444+
15445+
/* Warn about a template DEP that references a TU-local entity.
15446+
15447+
If STRICT, DEP only referenced entities from the GMF. Returns TRUE
15448+
if we explained anything. */
15449+
15450+
bool
15451+
depset::hash::diagnose_template_names_tu_local (depset *dep, bool strict)
15452+
{
15453+
tree decl = dep->get_entity ();
15454+
15455+
/* Don't bother walking if we know we won't be emitting anything. */
15456+
if (!warning_enabled_at (DECL_SOURCE_LOCATION (decl),
15457+
OPT_Wtemplate_names_tu_local)
15458+
/* Only warn strictly if users haven't silenced this warning here. */
15459+
|| (strict && !warning_enabled_at (DECL_SOURCE_LOCATION (decl),
15460+
OPT_Wexpose_global_module_tu_local)))
15461+
return false;
15462+
15463+
/* Friend decls in a class body are ignored, but this is harmless:
15464+
it should not impact any consumers. */
15465+
if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
15466+
return false;
15467+
15468+
/* We should now only be warning about templates. */
15469+
gcc_checking_assert
15470+
(TREE_CODE (decl) == TEMPLATE_DECL
15471+
&& VAR_OR_FUNCTION_DECL_P (DECL_TEMPLATE_RESULT (decl)));
15472+
15473+
/* Don't warn if we've seen any explicit instantiation definitions,
15474+
the intent might be for importers to only use those. */
15475+
if (template_has_explicit_inst (decl))
15476+
return false;
15477+
15478+
for (depset *rdep : dep->deps)
15479+
if (!rdep->is_binding () && rdep->is_tu_local (strict))
15480+
{
15481+
tree ref = rdep->get_entity ();
15482+
auto_diagnostic_group d;
15483+
if (strict)
15484+
{
15485+
if (warning_enabled_at (DECL_SOURCE_LOCATION (ref),
15486+
OPT_Wexpose_global_module_tu_local)
15487+
&& warning_at (DECL_SOURCE_LOCATION (decl),
15488+
OPT_Wtemplate_names_tu_local,
15489+
"%qD refers to TU-local entity %qD, which may "
15490+
"cause issues when instantiating in other TUs",
15491+
decl, ref))
15492+
{
15493+
is_tu_local_entity (ref, /*explain=*/true);
15494+
return true;
15495+
}
15496+
}
15497+
else if (warning_at (DECL_SOURCE_LOCATION (decl),
15498+
OPT_Wtemplate_names_tu_local,
15499+
"%qD refers to TU-local entity %qD and cannot "
15500+
"be instantiated in other TUs", decl, ref))
15501+
{
15502+
is_tu_local_entity (ref, /*explain=*/true);
15503+
return true;
15504+
}
15505+
}
15506+
15507+
return false;
15508+
}
15509+
1534515510
/* Sort the bindings, issue errors about bad internal refs. */
1534615511

1534715512
bool
@@ -15366,30 +15531,21 @@ depset::hash::finalize_dependencies ()
1536615531
if (CHECKING_P)
1536715532
for (depset *entity : dep->deps)
1536815533
gcc_checking_assert (!entity->is_import ());
15534+
continue;
1536915535
}
15370-
else if (dep->is_exposure () && !dep->is_tu_local ())
15371-
{
15372-
ok = false;
15373-
bool explained = false;
15374-
tree decl = dep->get_entity ();
1537515536

15376-
for (depset *rdep : dep->deps)
15377-
if (!rdep->is_binding ()
15378-
&& rdep->is_tu_local ()
15379-
&& !is_exposure_of_member_type (dep, rdep))
15380-
{
15381-
// FIXME:QOI Better location information? We're
15382-
// losing, so it doesn't matter about efficiency
15383-
tree exposed = rdep->get_entity ();
15384-
auto_diagnostic_group d;
15385-
error_at (DECL_SOURCE_LOCATION (decl),
15386-
"%qD exposes TU-local entity %qD", decl, exposed);
15387-
bool informed = is_tu_local_entity (exposed, /*explain=*/true);
15388-
gcc_checking_assert (informed);
15389-
explained = true;
15390-
break;
15391-
}
15537+
/* Otherwise, we'll check for bad internal refs.
15538+
Don't complain about any references from TU-local entities. */
15539+
if (dep->is_tu_local ())
15540+
continue;
15541+
15542+
if (dep->is_exposure ())
15543+
{
15544+
bool explained = diagnose_bad_internal_ref (dep);
1539215545

15546+
/* A TU-local variable will always be considered an exposure,
15547+
so we don't have to worry about strict-only handling. */
15548+
tree decl = dep->get_entity ();
1539315549
if (!explained
1539415550
&& VAR_P (decl)
1539515551
&& (DECL_DECLARED_CONSTEXPR_P (decl)
@@ -15414,42 +15570,34 @@ depset::hash::finalize_dependencies ()
1541415570
explained = true;
1541515571
}
1541615572

15417-
/* We should have emitted an error above. */
15573+
/* We should have emitted an error above, unless the warning was
15574+
silenced. */
1541815575
gcc_checking_assert (explained);
15576+
ok = false;
15577+
continue;
1541915578
}
15420-
else if (warn_template_names_tu_local
15421-
&& dep->refs_tu_local () && !dep->is_tu_local ())
15422-
{
15423-
tree decl = dep->get_entity ();
1542415579

15425-
/* Friend decls in a class body are ignored, but this is harmless:
15426-
it should not impact any consumers. */
15427-
if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)))
15428-
continue;
15429-
15430-
/* We should now only be warning about templates. */
15431-
gcc_checking_assert
15432-
(TREE_CODE (decl) == TEMPLATE_DECL
15433-
&& VAR_OR_FUNCTION_DECL_P (DECL_TEMPLATE_RESULT (decl)));
15580+
/* In all other cases, we're just warning (rather than erroring).
15581+
We don't want to do too much warning, so let's just bail after
15582+
the first warning we successfully emit. */
15583+
if (warn_expose_global_module_tu_local
15584+
&& !dep->is_tu_local (/*strict=*/true)
15585+
&& dep->is_exposure (/*strict=*/true)
15586+
&& diagnose_bad_internal_ref (dep, /*strict=*/true))
15587+
continue;
1543415588

15435-
/* Don't warn if we've seen any explicit instantiation definitions,
15436-
the intent might be for importers to only use those. */
15437-
if (template_has_explicit_inst (decl))
15438-
continue;
15589+
if (warn_template_names_tu_local
15590+
&& dep->refs_tu_local ()
15591+
&& diagnose_template_names_tu_local (dep))
15592+
continue;
1543915593

15440-
for (depset *rdep : dep->deps)
15441-
if (!rdep->is_binding () && rdep->is_tu_local ())
15442-
{
15443-
tree ref = rdep->get_entity ();
15444-
auto_diagnostic_group d;
15445-
if (warning_at (DECL_SOURCE_LOCATION (decl),
15446-
OPT_Wtemplate_names_tu_local,
15447-
"%qD refers to TU-local entity %qD and cannot "
15448-
"be instantiated in other TUs", decl, ref))
15449-
is_tu_local_entity (ref, /*explain=*/true);
15450-
break;
15451-
}
15452-
}
15594+
if (warn_template_names_tu_local
15595+
&& warn_expose_global_module_tu_local
15596+
&& !dep->is_tu_local (/*strict=*/true)
15597+
&& dep->refs_tu_local (/*strict=*/true)
15598+
&& !dep->is_exposure (/*strict=*/true)
15599+
&& diagnose_template_names_tu_local (dep, /*strict=*/true))
15600+
continue;
1545315601
}
1545415602

1545515603
return ok;

0 commit comments

Comments
 (0)