public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Nathaniel Shead <nathanieloshead@gmail.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org, Jason Merrill <jason@redhat.com>,
	Nathan Sidwell <nathan@acm.org>
Subject: [PATCH v3] c++/modules: Emit definitions of ODR-used static members imported from modules [PR112899]
Date: Sat, 20 Jan 2024 21:45:47 +1100	[thread overview]
Message-ID: <65aba461.a70a0220.7fd5c.ad24@mx.google.com> (raw)
In-Reply-To: <36bed0fe-5564-60ed-df91-240d78819add@idea>

On Fri, Jan 19, 2024 at 01:57:18PM -0500, Patrick Palka wrote:
> On Wed, 3 Jan 2024, Nathaniel Shead wrote:
> 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > 
> > -- >8 --
> > 
> > Static data members marked 'inline' should be emitted in TUs where they
> > are ODR-used.  We need to make sure that statics imported from modules
> > are correctly added to the 'pending_statics' map so that they get
> > emitted if needed, otherwise the attached testcase fails to link.
> > 
> > 	PR c++/112899
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-tree.h (note_variable_template_instantiation): Rename to...
> > 	(note_static_storage_variable): ...this.
> > 	* decl2.cc (note_variable_template_instantiation): Rename to...
> > 	(note_static_storage_variable): ...this.
> > 	* pt.cc (instantiate_decl): Rename usage of above function.
> > 	* module.cc (trees_in::read_var_def): Remember pending statics
> > 	that we stream in.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/modules/init-4_a.C: New test.
> > 	* g++.dg/modules/init-4_b.C: New test.
> > 
> > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> > ---
> >  gcc/cp/cp-tree.h                        |  2 +-
> >  gcc/cp/decl2.cc                         |  4 ++--
> >  gcc/cp/module.cc                        |  4 ++++
> >  gcc/cp/pt.cc                            |  2 +-
> >  gcc/testsuite/g++.dg/modules/init-4_a.C |  9 +++++++++
> >  gcc/testsuite/g++.dg/modules/init-4_b.C | 11 +++++++++++
> >  6 files changed, 28 insertions(+), 4 deletions(-)
> >  create mode 100644 gcc/testsuite/g++.dg/modules/init-4_a.C
> >  create mode 100644 gcc/testsuite/g++.dg/modules/init-4_b.C
> > 
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 1979572c365..ebd2850599a 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7113,7 +7113,7 @@ extern tree maybe_get_tls_wrapper_call		(tree);
> >  extern void mark_needed				(tree);
> >  extern bool decl_needed_p			(tree);
> >  extern void note_vague_linkage_fn		(tree);
> > -extern void note_variable_template_instantiation (tree);
> > +extern void note_static_storage_variable	(tree);
> >  extern tree build_artificial_parm		(tree, tree, tree);
> >  extern bool possibly_inlined_p			(tree);
> >  extern int parm_index                           (tree);
> > diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> > index 0850d3f5bce..241216b0dfe 100644
> > --- a/gcc/cp/decl2.cc
> > +++ b/gcc/cp/decl2.cc
> > @@ -910,10 +910,10 @@ note_vague_linkage_fn (tree decl)
> >    vec_safe_push (deferred_fns, decl);
> >  }
> >  
> > -/* As above, but for variable template instantiations.  */
> > +/* As above, but for variables with static storage duration.  */
> >  
> >  void
> > -note_variable_template_instantiation (tree decl)
> > +note_static_storage_variable (tree decl)
> >  {
> >    vec_safe_push (pending_statics, decl);
> >  }
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index 0bd46414da9..14818131a70 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -11752,6 +11752,10 @@ trees_in::read_var_def (tree decl, tree maybe_template)
> >  	  DECL_INITIALIZED_P (decl) = true;
> >  	  if (maybe_dup && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (maybe_dup))
> >  	    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
> > +	  if (DECL_CONTEXT (decl)
> > +	      && RECORD_OR_UNION_TYPE_P (DECL_CONTEXT (decl))
> > +	      && !DECL_TEMPLATE_INFO (decl))
> > +	    note_static_storage_variable (decl);
> 
> It seems this should also handle templated inlines via
> 
>    && (!DECL_TEMPLATE_INFO (decl)
>        || DECL_IMPLICIT_INSTANTIATION (decl))
> 
> otherwise the following fails to link:
> 
>   $ cat init-5_a.H
>   template<bool _DecOnly>
>   struct __from_chars_alnum_to_val_table {
>     static inline int value = 42;
>   };
> 
>   inline unsigned char
>   __from_chars_alnum_to_val() {
>     return __from_chars_alnum_to_val_table<false>::value;
>   }
> 
>   $ cat init-6_b.C
>   import "init-5_a.H";
> 
>   int main() {
>     __from_chars_alnum_to_val();
>   }
> 
>   $ g++ -fmodules-ts -std=c++20 init-5_a.H init-5_b.C
>   /usr/bin/ld: /tmp/ccNRaads.o: in function `__from_chars_alnum_to_val()':
>   init-6_b.C:(.text._Z25__from_chars_alnum_to_valv[_Z25__from_chars_alnum_to_valv]+0x6): undefined reference to `__from_chars_alnum_to_val_table<false>::value'

Ah yes, of course, since that's the other context where declarations are
added to 'pending_statics'.

> 
> By the way I ran into this when testing out std::print with modules:
> 
>   $ cat std.C
>   export module std;
>   export import <bits/stdc++.h>;
> 
>   $ cat hello.C
>   import std;
> 
>   int main() {
>     std::print("Hello {}!", "World");
>   }
> 
>   $ g++ -fmodules-ts -std=c++26 -x c++-system-header bits/stdc++.h
>   $ g++ -fmodules-ts -std=c++26 std.C hello.C && ./a.out # before
>   /usr/bin/ld: /tmp/ccqNgOM1.o: in function `unsigned char std::__detail::__from_chars_alnum_to_val<false>(unsigned char)':
>   hello.C:(.text._ZNSt8__detail25__from_chars_alnum_to_valILb0EEEhh[_ZNSt8__detail25__from_chars_alnum_to_valILb0EEEhh]+0x12): undefined reference to `std::__detail::__from_chars_alnum_to_val_table<false>::value'
>   $ g++ -fmodules-ts -std=c++26 std.C hello.C && ./a.out # after
>   Hello World!
> 
> It's great that this is so close to working!

That's great!

Here's a new version of the patch with the above fixed. I also included
your change to only add class variable templates to 'pending_statics'
(and the normal 'static_decl's for non-class otherwise) as otherwise I
could imagine that they would cause issues with this later too.

I know that there's been discussion about the correct ABI for inline
declarations, but personally I'd like to have this fixed for normal uses
in GCC14 at least, and we can revisit the specific cases where various
kinds of declarations are emitted in stage 1.

Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

P.S.  As I go to send this, I wonder if maybe something like
'note_static_member_variable' would be a clearer choice of name than
'note_static_storage_variable'?

-- >8 --

Static data members marked 'inline' should be emitted in TUs where they
are ODR-used.  We need to make sure that statics imported from modules
are correctly added to the 'pending_statics' map so that they get
emitted if needed, otherwise the attached testcase fails to link.

	PR c++/112899

gcc/cp/ChangeLog:

	* cp-tree.h (note_variable_template_instantiation): Rename to...
	(note_static_storage_variable): ...this.
	* decl2.cc (note_variable_template_instantiation): Rename to...
	(note_static_storage_variable): ...this.
	* pt.cc (instantiate_decl): Rename usage of above function and
	only use for class-scope variables.
	* module.cc (trees_in::read_var_def): Remember pending statics
	that we stream in.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/init-4_a.C: New test.
	* g++.dg/modules/init-4_b.C: New test.
	* g++.dg/modules/init-6_a.H: New test.
	* g++.dg/modules/init-6_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Patrick Palka <ppalka@redhat.com>
---
 gcc/cp/cp-tree.h                        |  2 +-
 gcc/cp/decl2.cc                         |  4 ++--
 gcc/cp/module.cc                        |  5 +++++
 gcc/cp/pt.cc                            |  7 ++++++-
 gcc/testsuite/g++.dg/modules/init-4_a.C |  9 +++++++++
 gcc/testsuite/g++.dg/modules/init-4_b.C | 11 +++++++++++
 gcc/testsuite/g++.dg/modules/init-6_a.H | 12 ++++++++++++
 gcc/testsuite/g++.dg/modules/init-6_b.C |  8 ++++++++
 8 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/init-4_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/init-4_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/init-6_a.H
 create mode 100644 gcc/testsuite/g++.dg/modules/init-6_b.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d9b14d7c4f5..ac7531c5a73 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7146,7 +7146,7 @@ extern tree maybe_get_tls_wrapper_call		(tree);
 extern void mark_needed				(tree);
 extern bool decl_needed_p			(tree);
 extern void note_vague_linkage_fn		(tree);
-extern void note_variable_template_instantiation (tree);
+extern void note_static_storage_variable	(tree);
 extern tree build_artificial_parm		(tree, tree, tree);
 extern bool possibly_inlined_p			(tree);
 extern int parm_index                           (tree);
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 91d4e2e5ea4..5d270bf2cdc 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -947,10 +947,10 @@ note_vague_linkage_fn (tree decl)
   vec_safe_push (deferred_fns, decl);
 }
 
-/* As above, but for variable template instantiations.  */
+/* As above, but for variables with static storage duration.  */
 
 void
-note_variable_template_instantiation (tree decl)
+note_static_storage_variable (tree decl)
 {
   vec_safe_push (pending_statics, decl);
 }
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 8db662c0267..b68407a3499 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -11775,6 +11775,11 @@ trees_in::read_var_def (tree decl, tree maybe_template)
 	  DECL_INITIALIZED_P (decl) = true;
 	  if (maybe_dup && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (maybe_dup))
 	    DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
+	  if (DECL_CLASS_SCOPE_P (decl)
+	      && !DECL_VTABLE_OR_VTT_P (decl)
+	      && (!DECL_TEMPLATE_INFO (decl)
+		  || DECL_IMPLICIT_INSTANTIATION (decl)))
+	    note_static_storage_variable (decl);
 	}
       DECL_INITIAL (decl) = init;
       if (!dyn_init)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index f82d018c981..7c38594b82d 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -27256,7 +27256,12 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
     {
       set_instantiating_module (d);
       if (variable_template_p (gen_tmpl))
-	note_variable_template_instantiation (d);
+	{
+	  if (DECL_CLASS_SCOPE_P (d))
+	    note_static_storage_variable (d);
+	  else
+	    vec_safe_push (static_decls, d);
+	}
       instantiate_body (td, args, d, false);
     }
 
diff --git a/gcc/testsuite/g++.dg/modules/init-4_a.C b/gcc/testsuite/g++.dg/modules/init-4_a.C
new file mode 100644
index 00000000000..e0eb97b474e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/init-4_a.C
@@ -0,0 +1,9 @@
+// PR c++/112899
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi M }
+
+export module M;
+
+export struct A {
+  static constexpr int x = -1;
+};
diff --git a/gcc/testsuite/g++.dg/modules/init-4_b.C b/gcc/testsuite/g++.dg/modules/init-4_b.C
new file mode 100644
index 00000000000..d28017a1d14
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/init-4_b.C
@@ -0,0 +1,11 @@
+// PR c++/112899
+// { dg-module-do run }
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+int main() {
+  const int& x = A::x;
+  if (x != -1)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/modules/init-6_a.H b/gcc/testsuite/g++.dg/modules/init-6_a.H
new file mode 100644
index 00000000000..a48d90d7aa7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/init-6_a.H
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi {} }
+
+template <bool _DecOnly>
+struct __from_chars_alnum_to_val_table {
+  static inline int value = 42;
+};
+
+inline unsigned char
+__from_chars_alnum_to_val() {
+  return __from_chars_alnum_to_val_table<false>::value;
+}
diff --git a/gcc/testsuite/g++.dg/modules/init-6_b.C b/gcc/testsuite/g++.dg/modules/init-6_b.C
new file mode 100644
index 00000000000..6bb0e83c3ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/init-6_b.C
@@ -0,0 +1,8 @@
+// { dg-module-do link }
+// { dg-additional-options "-fmodules-ts" }
+
+import "init-6_a.H";
+
+int main() {
+  __from_chars_alnum_to_val();
+}
-- 
2.43.0


  reply	other threads:[~2024-01-20 10:45 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-02 22:40 Nathaniel Shead
2024-01-02 22:45 ` [PATCH] " Nathaniel Shead
2024-01-03 12:42   ` [PATCH v2] " Nathaniel Shead
2024-01-04 19:16     ` :Re: " Patrick Palka
2024-01-04 20:31 ` Jason Merrill
2024-01-04 22:24   ` Nathaniel Shead
2024-01-04 22:42     ` Jason Merrill
2024-01-04 23:02       ` Nathaniel Shead
2024-01-05  2:06         ` Jason Merrill
2024-01-06 22:30           ` Nathan Sidwell
2024-01-08  9:21             ` Iain Sandoe
2024-01-08 22:38               ` Jason Merrill
2024-01-19 18:57 ` Patrick Palka
2024-01-20 10:45   ` Nathaniel Shead [this message]
2024-01-24 20:24     ` [PATCH v3] " Jason Merrill
2024-01-26  2:28       ` [PATCH v4] " Nathaniel Shead
2024-01-26  3:35         ` Jason Merrill

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=65aba461.a70a0220.7fd5c.ad24@mx.google.com \
    --to=nathanieloshead@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=nathan@acm.org \
    --cc=ppalka@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).