c++/modules: Fix linkage checks for exported using-decls
This fixes some inconsistencies with what kinds of linkage various entities are assumed to have. This also fixes handling of exported using-decls binding to GM entities and type aliases to better align with the standard's requirements. gcc/cp/ChangeLog: * name-lookup.cc (check_can_export_using_decl): Handle internal linkage GM entities (but ignore in header units); use linkage of entity ultimately referred to by aliases. gcc/testsuite/ChangeLog: * g++.dg/modules/using-10.C: Add tests for no-linkage, fix expected linkage of aliases. * g++.dg/modules/using-12.C: Likewise. * g++.dg/modules/using-27.C: New test. * g++.dg/modules/using-28_a.C: New test. * g++.dg/modules/using-28_b.C: New test. * g++.dg/modules/using-29.H: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
parent
ad08ef098a
commit
d0762e93ce
7 changed files with 159 additions and 46 deletions
|
@ -5206,38 +5206,47 @@ pushdecl_outermost_localscope (tree x)
|
|||
static bool
|
||||
check_can_export_using_decl (tree binding)
|
||||
{
|
||||
tree decl = STRIP_TEMPLATE (binding);
|
||||
/* Declarations in header units are always OK. */
|
||||
if (header_module_p ())
|
||||
return true;
|
||||
|
||||
/* Linkage is determined by the owner of an enumerator. */
|
||||
if (TREE_CODE (decl) == CONST_DECL)
|
||||
decl = TYPE_NAME (DECL_CONTEXT (decl));
|
||||
|
||||
/* If the using decl is exported, the things it refers
|
||||
to must also be exported (or not have module attachment). */
|
||||
if (!DECL_MODULE_EXPORT_P (decl)
|
||||
&& (DECL_LANG_SPECIFIC (decl)
|
||||
&& DECL_MODULE_ATTACH_P (decl)))
|
||||
/* We want the linkage of the underlying entity, so strip typedefs.
|
||||
If the underlying entity is a builtin type then we're OK. */
|
||||
tree entity = binding;
|
||||
if (TREE_CODE (entity) == TYPE_DECL)
|
||||
{
|
||||
bool internal_p = !TREE_PUBLIC (decl);
|
||||
entity = TYPE_MAIN_DECL (TREE_TYPE (entity));
|
||||
if (!entity)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* A template in an anonymous namespace doesn't constrain TREE_PUBLIC
|
||||
until it's instantiated, so double-check its context. */
|
||||
if (!internal_p && TREE_CODE (binding) == TEMPLATE_DECL)
|
||||
internal_p = decl_internal_context_p (decl);
|
||||
linkage_kind linkage = decl_linkage (entity);
|
||||
tree not_tmpl = STRIP_TEMPLATE (entity);
|
||||
|
||||
/* Attachment is determined by the owner of an enumerator. */
|
||||
if (TREE_CODE (not_tmpl) == CONST_DECL)
|
||||
not_tmpl = TYPE_NAME (DECL_CONTEXT (not_tmpl));
|
||||
|
||||
/* If the using decl is exported, the things it refers to must
|
||||
have external linkage. decl_linkage returns lk_external for
|
||||
module linkage so also check for attachment. */
|
||||
if (linkage != lk_external
|
||||
|| (DECL_LANG_SPECIFIC (not_tmpl)
|
||||
&& DECL_MODULE_ATTACH_P (not_tmpl)
|
||||
&& !DECL_MODULE_EXPORT_P (not_tmpl)))
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
error ("exporting %q#D that does not have external linkage",
|
||||
binding);
|
||||
if (TREE_CODE (decl) == TYPE_DECL && !DECL_IMPLICIT_TYPEDEF_P (decl))
|
||||
/* An un-exported explicit type alias has no linkage. */
|
||||
inform (DECL_SOURCE_LOCATION (binding),
|
||||
"%q#D declared here with no linkage", binding);
|
||||
else if (internal_p)
|
||||
inform (DECL_SOURCE_LOCATION (binding),
|
||||
"%q#D declared here with internal linkage", binding);
|
||||
if (linkage == lk_none)
|
||||
inform (DECL_SOURCE_LOCATION (entity),
|
||||
"%q#D declared here with no linkage", entity);
|
||||
else if (linkage == lk_internal)
|
||||
inform (DECL_SOURCE_LOCATION (entity),
|
||||
"%q#D declared here with internal linkage", entity);
|
||||
else
|
||||
inform (DECL_SOURCE_LOCATION (binding),
|
||||
"%q#D declared here with module linkage", binding);
|
||||
inform (DECL_SOURCE_LOCATION (entity),
|
||||
"%q#D declared here with module linkage", entity);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,13 @@ namespace s {
|
|||
}
|
||||
}
|
||||
|
||||
export using s::a1; // { dg-error "does not have external linkage" }
|
||||
export using s::b1; // { dg-error "does not have external linkage" }
|
||||
export using s::x1; // { dg-error "does not have external linkage" }
|
||||
export using s::y1; // { dg-error "does not have external linkage" }
|
||||
export using s::f1; // { dg-error "does not have external linkage" }
|
||||
export using s::g1; // { dg-error "does not have external linkage" }
|
||||
|
||||
// module linkage
|
||||
namespace m {
|
||||
struct a2 {}; // { dg-message "declared here with module linkage" }
|
||||
|
@ -41,13 +48,6 @@ namespace m {
|
|||
void g2(); // { dg-message "declared here with module linkage" }
|
||||
}
|
||||
|
||||
export using s::a1; // { dg-error "does not have external linkage" }
|
||||
export using s::b1; // { dg-error "does not have external linkage" }
|
||||
export using s::x1; // { dg-error "does not have external linkage" }
|
||||
export using s::y1; // { dg-error "does not have external linkage" }
|
||||
export using s::f1; // { dg-error "does not have external linkage" }
|
||||
export using s::g1; // { dg-error "does not have external linkage" }
|
||||
|
||||
export using m::a2; // { dg-error "does not have external linkage" }
|
||||
export using m::b2; // { dg-error "does not have external linkage" }
|
||||
export using m::x2; // { dg-error "does not have external linkage" }
|
||||
|
@ -55,15 +55,47 @@ export using m::y2; // { dg-error "does not have external linkage" }
|
|||
export using m::f2; // { dg-error "does not have external linkage" }
|
||||
export using m::g2; // { dg-error "does not have external linkage" }
|
||||
|
||||
namespace t {
|
||||
using a = int; // { dg-message "declared here with no linkage" }
|
||||
// no linkage
|
||||
namespace n {
|
||||
using a3 = struct { int x; }; // { dg-message "declared here with no linkage" }
|
||||
|
||||
template <typename T>
|
||||
using b = int; // { dg-message "declared here with no linkage" }
|
||||
struct {} tmp_s; // { dg-message "declared here with no linkage" }
|
||||
using b3 = decltype(tmp_s);
|
||||
|
||||
typedef int c; // { dg-message "declared here with no linkage" }
|
||||
enum {} tmp_e; // { dg-message "declared here with no linkage" }
|
||||
using c3 = decltype(tmp_e);
|
||||
|
||||
auto foo() {
|
||||
struct s {}; // { dg-message "declared here with no linkage" }
|
||||
return s{};
|
||||
}
|
||||
using d3 = decltype(foo());
|
||||
}
|
||||
|
||||
export using t::a; // { dg-error "does not have external linkage" }
|
||||
export using t::b; // { dg-error "does not have external linkage" }
|
||||
export using n::a3; // { dg-error "does not have external linkage" }
|
||||
export using n::b3; // { dg-error "does not have external linkage" }
|
||||
export using n::c3; // { dg-error "does not have external linkage" }
|
||||
export using n::d3; // { dg-error "does not have external linkage" }
|
||||
|
||||
// typedefs
|
||||
namespace t {
|
||||
// aliases have the linkage of the entity they ultimately refer to
|
||||
using a = int;
|
||||
typedef a b;
|
||||
|
||||
// a template is not an alias
|
||||
template <typename T>
|
||||
using c = int; // { dg-message "declared here with module linkage" }
|
||||
|
||||
// anonymous type with typedef name for linkage purposes
|
||||
typedef struct {} d; // { dg-message "declared here with module linkage" }
|
||||
|
||||
// non-empty enum gets linkage of enumerator name
|
||||
enum { X } e; // { dg-message "declared here with module linkage"}
|
||||
}
|
||||
|
||||
export using t::a;
|
||||
export using t::b;
|
||||
export using t::c; // { dg-error "does not have external linkage" }
|
||||
export using t::d; // { dg-error "does not have external linkage" }
|
||||
export using t::e; // { dg-error "does not have external linkage" }
|
||||
|
|
|
@ -57,15 +57,47 @@ namespace m {
|
|||
export using m::g2; // { dg-error "does not have external linkage" }
|
||||
}
|
||||
|
||||
namespace t {
|
||||
using a = int; // { dg-message "declared here with no linkage" }
|
||||
// no linkage
|
||||
namespace n {
|
||||
using a3 = struct { int x; }; // { dg-message "declared here with no linkage" }
|
||||
|
||||
template <typename T>
|
||||
using b = int; // { dg-message "declared here with no linkage" }
|
||||
struct {} tmp_s; // { dg-message "declared here with no linkage" }
|
||||
using b3 = decltype(tmp_s);
|
||||
|
||||
typedef int c; // { dg-message "declared here with no linkage" }
|
||||
enum {} tmp_e; // { dg-message "declared here with no linkage" }
|
||||
using c3 = decltype(tmp_e);
|
||||
|
||||
export using t::a; // { dg-error "does not have external linkage" }
|
||||
export using t::b; // { dg-error "does not have external linkage" }
|
||||
export using t::c; // { dg-error "does not have external linkage" }
|
||||
auto foo() {
|
||||
struct s {}; // { dg-message "declared here with no linkage" }
|
||||
return s{};
|
||||
}
|
||||
using d3 = decltype(foo());
|
||||
|
||||
export using n::a3; // { dg-error "does not have external linkage" }
|
||||
export using n::b3; // { dg-error "does not have external linkage" }
|
||||
export using n::c3; // { dg-error "does not have external linkage" }
|
||||
export using n::d3; // { dg-error "does not have external linkage" }
|
||||
}
|
||||
|
||||
// typedefs
|
||||
namespace t {
|
||||
// aliases have the linkage of the entity they ultimately refer to
|
||||
using a = int;
|
||||
typedef a b;
|
||||
|
||||
// a template is not an alias
|
||||
template <typename T>
|
||||
using c = int; // { dg-message "declared here with module linkage" }
|
||||
|
||||
// anonymous type with typedef name for linkage purposes
|
||||
typedef struct {} d; // { dg-message "declared here with module linkage" }
|
||||
|
||||
// non-empty enum gets linkage of enumerator name
|
||||
enum { X } e; // { dg-message "declared here with module linkage" }
|
||||
|
||||
export using t::a;
|
||||
export using t::b;
|
||||
export using t::c; // { dg-error "does not have external linkage" }
|
||||
export using t::d; // { dg-error "does not have external linkage" }
|
||||
export using t::e; // { dg-error "does not have external linkage" }
|
||||
}
|
||||
|
|
14
gcc/testsuite/g++.dg/modules/using-27.C
Normal file
14
gcc/testsuite/g++.dg/modules/using-27.C
Normal file
|
@ -0,0 +1,14 @@
|
|||
// { dg-additional-options "-fmodules-ts -Wno-global-module" }
|
||||
// { dg-module-cmi !bad }
|
||||
|
||||
module;
|
||||
|
||||
static int x = 123; // { dg-message "declared here with internal linkage" }
|
||||
static void f() {} // { dg-message "declared here with internal linkage" }
|
||||
using T = struct {}; // { dg-message "declared here with no linkage" }
|
||||
|
||||
export module bad;
|
||||
|
||||
export using ::x; // { dg-error "does not have external linkage" }
|
||||
export using ::f; // { dg-error "does not have external linkage" }
|
||||
export using ::T; // { dg-error "does not have external linkage" }
|
12
gcc/testsuite/g++.dg/modules/using-28_a.C
Normal file
12
gcc/testsuite/g++.dg/modules/using-28_a.C
Normal file
|
@ -0,0 +1,12 @@
|
|||
// { dg-additional-options "-fmodules-ts -Wno-global-module" }
|
||||
// { dg-module-cmi M }
|
||||
// Test that typedef names correctly provide external linkage
|
||||
|
||||
module;
|
||||
typedef struct { int x; } A;
|
||||
export module M;
|
||||
|
||||
export typedef struct {} B;
|
||||
|
||||
export using ::A;
|
||||
export using ::B;
|
8
gcc/testsuite/g++.dg/modules/using-28_b.C
Normal file
8
gcc/testsuite/g++.dg/modules/using-28_b.C
Normal file
|
@ -0,0 +1,8 @@
|
|||
// { dg-additional-options "-fmodules-ts" }
|
||||
|
||||
import M;
|
||||
|
||||
int main() {
|
||||
A a { 10 };
|
||||
B b;
|
||||
}
|
6
gcc/testsuite/g++.dg/modules/using-29.H
Normal file
6
gcc/testsuite/g++.dg/modules/using-29.H
Normal file
|
@ -0,0 +1,6 @@
|
|||
// { dg-additional-options "-fmodule-header" }
|
||||
|
||||
static int foo = 123;
|
||||
namespace ns {
|
||||
using ::foo; // OK, we're in a header unit
|
||||
}
|
Loading…
Add table
Reference in a new issue