diff mbox series

[PR,C++,94426] Lambda linkage

Message ID 32e1aca1-2d0a-8973-6620-9df466ca7ad5@acm.org
State New
Headers show
Series [PR,C++,94426] Lambda linkage | expand

Commit Message

Nathan Sidwell April 10, 2020, 6:08 p.m. UTC
My fix for 94147 was confusing no-linkage with internal linkage, at the 
language level.  That's wrong. (the std is confusing here, because it 
describes linkage of names (which is wrong), and lambdas have no names)

Lambdas with extra-scope, have linkage.  However, at the 
implementation-level that linkage is at least as restricted as the 
linkage of the extra-scope decl.

Further, when instantiating a variable initialized by a lambda, we must 
determine the visibility of the variable itself, before instantiating 
its initializer.  If the template arguments are internal (or 
no-linkage), the variable will have internal linkage, regardless of the 
linkage of the template it is instantiated from.  We need to know that 
before instantiating the lambda, so we can restrict its linkage correctly.

I'll commit this in a few days.

nathan

Comments

Iain Sandoe April 14, 2020, 3:40 p.m. UTC | #1
Hi Nathan,

Nathan Sidwell <nathan@acm.org> wrote:

> My fix for 94147 was confusing no-linkage with internal linkage, at the language level.  That's wrong. (the std is confusing here, because it describes linkage of names (which is wrong), and lambdas have no names)
> 
> Lambdas with extra-scope, have linkage.  However, at the implementation-level that linkage is at least as restricted as the linkage of the extra-scope decl.
> 
> Further, when instantiating a variable initialized by a lambda, we must determine the visibility of the variable itself, before instantiating its initializer.  If the template arguments are internal (or no-linkage), the variable will have internal linkage, regardless of the linkage of the template it is instantiated from.  We need to know that before instantiating the lambda, so we can restrict its linkage correctly.
> 
> I'll commit this in a few days.

As discussed on irc,

The testcase for this fails on Darwin, where we don’t use .local or .comm for the var.

I’ve tested this on x86-64-linux and darwin, 
but I plan on testing on a few more Darwin boxen,
OK to apply, if additional testing passes?

thanks
Iain

gcc/testsuite/ChangeLog:

2020-04-14  Iain Sandoe  <iain@sandoe.co.uk>

	* g++.dg/cpp0x/lambda/pr94426-2.C: Add tests for Darwin’s codegen.

iff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
index 3db864c604b..fc85400388a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
@@ -16,5 +16,10 @@ void q ()
 }
 
 // The instantiation of VAR becomes local
-// { dg-final { scan-assembler {.local	_Z3VARIZ1qvEUlvE_E} { target { i?86-*-* x86_64-*-* } } } }
-// { dg-final { scan-assembler {.comm	_Z3VARIZ1qvEUlvE_E,1,1} { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler {.local	_Z3VARIZ1qvEUlvE_E} { target { { i?86-*-* x86_64-*-* } && { ! *-*-darwin* } } } } }
+// { dg-final { scan-assembler {.comm	_Z3VARIZ1qvEUlvE_E,1,1} { target { { i?86-*-* x86_64-*-* } && { ! *-*-darwin* } } } } }
+
+// Darwin's assembler does not use .local (linker-visible symbols which are
+// not global are counted as local).
+// { dg-final { scan-assembler-not {.globl[ \t]+__Z3VARIZ1qvEUlvE_E} { target *-*-darwin* } } }
+// { dg-final { scan-assembler {.static_data[ \n\t]+__Z3VARIZ1qvEUlvE_E} { target *-*-darwin* } } }
Iain Sandoe April 16, 2020, 10:50 a.m. UTC | #2
Hi Nathan,

Iain Sandoe <idsandoe@googlemail.com> wrote:

> Nathan Sidwell <nathan@acm.org> wrote:
> 
>> My fix for 94147 was confusing no-linkage with internal linkage, at the language level.  That's wrong. (the std is confusing here, because it describes linkage of names (which is wrong), and lambdas have no names)
>> 
>> Lambdas with extra-scope, have linkage.  However, at the implementation-level that linkage is at least as restricted as the linkage of the extra-scope decl.
>> 
>> Further, when instantiating a variable initialized by a lambda, we must determine the visibility of the variable itself, before instantiating its initializer.  If the template arguments are internal (or no-linkage), the variable will have internal linkage, regardless of the linkage of the template it is instantiated from.  We need to know that before instantiating the lambda, so we can restrict its linkage correctly.
>> 
>> I'll commit this in a few days.
> 
> As discussed on irc,
> 
> The testcase for this fails on Darwin, where we don’t use .local or .comm for the var.
> 
> I’ve tested this on x86-64-linux and darwin, 
> but I plan on testing on a few more Darwin boxen,
> OK to apply, if additional testing passes?

that testing revealed some differences in storage description for the variable (powerpc 32b darwin puts it in bss, like linux, but the remainer of the platform versions use .static_data).  However, that’s not the relevant observation.

the observation is that the storage and symbols for 

_Z3VARIZ1qvEUlvE_E

has not changed between gcc-9 and trunk.

What has changed is the function that initializes that variable:

_Z4InitIN3VARIZ1qvEUlvE_EUlvE_EEbT_

which was weak / comdat [Linux]  weak / global [Darwin] and now is text section local (which is what I understood was the intention of the change) .. so I wonder if the scan-asms are testing what you intended?

how about the following - where IMO, from the observation above, the first two tests are not especially useful and could be removed.

the remainder of the amendments cater for USER_LABEL_PREFIX and a different spelling for ‘weak’ in the Darwin assembly language.

So - this now tests that the symbol exists, is spelled the way you intend and is not weak (or global on Darwin).

WDYT?
Iain

diff --cc gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
index 3db864c604b,3db864c604b..dd94ea1f325
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
@@@ -16,5 -16,5 +16,11 @@@ void q (
  }
  
  // The instantiation of VAR becomes local
--// { dg-final { scan-assembler {.local        _Z3VARIZ1qvEUlvE_E} { target { i?86-*-* x86_64-*-* } } } }
--// { dg-final { scan-assembler {.comm _Z3VARIZ1qvEUlvE_E,1,1} { target { i?86-*-* x86_64-*-* } } } }
++// { dg-final { scan-assembler {.local        _Z3VARIZ1qvEUlvE_E} { target { { i?86-*-* x86_64-*-* } && { ! *-*-darwin* } } } } }
++// { dg-final { scan-assembler {.comm _Z3VARIZ1qvEUlvE_E,1,1} { target { { i?86-*-* x86_64-*-* } && { ! *-*-darwin* } } } } }
++
++// The instantiation of VAR becomes local
++// { dg-final { scan-assembler-not {.globl[ \t]+_?_Z4InitIN3VARIZ1qvEUlvE_EUlvE_EEbT_} { target *-*-darwin* } } }
++// { dg-final { scan-assembler-not {.weak(_definition)?[ \t]+_?_Z4InitIN3VARIZ1qvEUlvE_EUlvE_EEbT_} { target  i?86-*-* x86_64-*-* *-*-darwin* } } }
++// Make sure it is defined with the mangling we expect.
++// { dg-final { scan-assembler {_?_Z4InitIN3VARIZ1qvEUlvE_EUlvE_EEbT_:} { target  i?86-*-* x86_64-*-* *-*-darwin* } } }
Nathan Sidwell April 16, 2020, 1:38 p.m. UTC | #3
On 4/16/20 6:50 AM, Iain Sandoe wrote:
> Hi Nathan,
> 
> Iain Sandoe <idsandoe@googlemail.com> wrote:
> 
>> Nathan Sidwell <nathan@acm.org> wrote:
>>
>>> My fix for 94147 was confusing no-linkage with internal linkage, at the language level.  That's wrong. (the std is confusing here, because it describes linkage of names (which is wrong), and lambdas have no names)
>>>
>>> Lambdas with extra-scope, have linkage.  However, at the implementation-level that linkage is at least as restricted as the linkage of the extra-scope decl.
>>>
>>> Further, when instantiating a variable initialized by a lambda, we must determine the visibility of the variable itself, before instantiating its initializer.  If the template arguments are internal (or no-linkage), the variable will have internal linkage, regardless of the linkage of the template it is instantiated from.  We need to know that before instantiating the lambda, so we can restrict its linkage correctly.
>>>
>>> I'll commit this in a few days.
>>
>> As discussed on irc,
>>
>> The testcase for this fails on Darwin, where we don’t use .local or .comm for the var.
>>
>> I’ve tested this on x86-64-linux and darwin,
>> but I plan on testing on a few more Darwin boxen,
>> OK to apply, if additional testing passes?
> 
> that testing revealed some differences in storage description for the variable (powerpc 32b darwin puts it in bss, like linux, but the remainer of the platform versions use .static_data).  However, that’s not the relevant observation.
> 
> the observation is that the storage and symbols for
> 
> _Z3VARIZ1qvEUlvE_E
> 
> has not changed between gcc-9 and trunk.
> 
> What has changed is the function that initializes that variable:
> 
> _Z4InitIN3VARIZ1qvEUlvE_EUlvE_EEbT_
> 
> which was weak / comdat [Linux]  weak / global [Darwin] and now is text section local (which is what I understood was the intention of the change) .. so I wonder if the scan-asms are testing what you intended?

That is indeed the expected change.

> how about the following - where IMO, from the observation above, the first two tests are not especially useful and could be removed.
> 
> the remainder of the amendments cater for USER_LABEL_PREFIX and a different spelling for ‘weak’ in the Darwin assembly language.
> 
> So - this now tests that the symbol exists, is spelled the way you intend and is not weak (or global on Darwin).

> 
> WDYT?

Thanks for checking, please apply.

(My secret plan worked! someone generalized the regexps!)
diff mbox series

Patch

2020-04-10  Nathan Sidwell  <nathan@acm.org>

	PR c++/94426 = lambdas with internal linkage are different to no-linkage
	* decl2.c (determine_visibility): A lambda's visibility is
	affected by its extra scope.
	* pt.c (instantiate_decl): Determine var's visibility before
	instantiating its initializer.
	* tree.c (no_linkage_check): Revert code looking at visibility of
	lambda's extra scope.
	gcc/cp/
	* g++.dg/cpp0x/lambda/pr94426-[12].C: New.
	* g++.dg/abi/lambda-vis.C: Drop a warning.
	* g++.dg/cpp0x/lambda/lambda-mangle.C: Lambda visibility on
	variable changes.
	* g++.dg/opt/dump1.C: Drop warnings of no import.

diff --git c/gcc/cp/decl2.c w/gcc/cp/decl2.c
index 6cf72b432e2..293df990435 100644
--- c/gcc/cp/decl2.c
+++ w/gcc/cp/decl2.c
@@ -2527,6 +2527,21 @@  determine_visibility (tree decl)
   else if (DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (decl))
     template_decl = decl;
 
+  if (TREE_CODE (decl) == TYPE_DECL
+      && LAMBDA_TYPE_P (TREE_TYPE (decl))
+      && CLASSTYPE_LAMBDA_EXPR (TREE_TYPE (decl)) != error_mark_node)
+    if (tree extra = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (decl)))
+      {
+	/* The lambda's visibility is limited by that of its extra
+	   scope.  */
+	int vis = 0;
+	if (TYPE_P (extra))
+	  vis = type_visibility (extra);
+	else
+	  vis = expr_visibility (extra);
+	constrain_visibility (decl, vis, false);
+      }
+
   /* If DECL is a member of a class, visibility specifiers on the
      class can influence the visibility of the DECL.  */
   tree class_type = NULL_TREE;
diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c
index 050a57b2e2e..0a8ec3198d2 100644
--- c/gcc/cp/pt.c
+++ w/gcc/cp/pt.c
@@ -25541,6 +25541,14 @@  instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
       c_inhibit_evaluation_warnings = 0;
     }
 
+  if (VAR_P (d))
+    {
+      /* The variable might be a lambda's extra scope, and that
+	 lambda's visibility depends on D's.  */
+      maybe_commonize_var (d);
+      determine_visibility (d);
+    }
+
   /* Mark D as instantiated so that recursive calls to
      instantiate_decl do not try to instantiate it again.  */
   DECL_TEMPLATE_INSTANTIATED (d) = 1;
diff --git c/gcc/cp/tree.c w/gcc/cp/tree.c
index d1192b7e094..1d311b0fe61 100644
--- c/gcc/cp/tree.c
+++ w/gcc/cp/tree.c
@@ -2780,9 +2780,10 @@  verify_stmt_tree (tree t)
   cp_walk_tree (&t, verify_stmt_tree_r, &statements, NULL);
 }
 
-/* Check if the type T depends on a type with no linkage and if so, return
-   it.  If RELAXED_P then do not consider a class type declared within
-   a vague-linkage function to have no linkage.  */
+/* Check if the type T depends on a type with no linkage and if so,
+   return it.  If RELAXED_P then do not consider a class type declared
+   within a vague-linkage function to have no linkage.  Remember:
+   no-linkage is not the same as internal-linkage*/
 
 tree
 no_linkage_check (tree t, bool relaxed_p)
@@ -2801,17 +2802,6 @@  no_linkage_check (tree t, bool relaxed_p)
       tree extra = LAMBDA_TYPE_EXTRA_SCOPE (t);
       if (!extra)
 	return t;
-
-      /* If the mangling scope is internal-linkage or not repeatable
-	 elsewhere, the lambda effectively has no linkage.  (Sadly
-	 we're not very careful with the linkages of types.)  */
-      if (TREE_CODE (extra) == VAR_DECL
-	  && !(TREE_PUBLIC (extra)
-	       && (processing_template_decl
-		   || (DECL_LANG_SPECIFIC (extra) && DECL_USE_TEMPLATE (extra))
-		   /* DECL_COMDAT is set too late for us to check.  */
-		   || DECL_VAR_DECLARED_INLINE_P (extra))))
-	return t;
     }
 
   /* Otherwise there's no point in checking linkage on template functions; we
diff --git c/gcc/testsuite/g++.dg/abi/lambda-vis.C w/gcc/testsuite/g++.dg/abi/lambda-vis.C
index 89683b2076a..c1033f501a3 100644
--- c/gcc/testsuite/g++.dg/abi/lambda-vis.C
+++ w/gcc/testsuite/g++.dg/abi/lambda-vis.C
@@ -2,7 +2,7 @@ 
 // { dg-options "-fno-inline" }
 
 template<typename T> int sfoo (T); // { dg-warning "used but never defined" }
-template<typename T> int gfoo (T); // { dg-warning "used but never defined" }
+template<typename T> int gfoo (T); // OK, but not completable
 template<typename T> int ifoo (T); // OK
 template<typename T> struct Wrapper {};
 template<typename T> Wrapper<T> capture (T &&) {return Wrapper<T> ();}
diff --git c/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C w/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
index 7894ef3051e..ef4bad8698b 100644
--- c/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
+++ w/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-mangle.C
@@ -54,9 +54,12 @@  void bar()
   []{}();
 }
 
-// lambdas used in non-template, non-class body initializers are internal.
+// lambdas used in namespace-scope initializers have the linkage of
+// the decl
 // { dg-final { scan-assembler-not "weak\[^\n\r\]*_ZNKUlv" } }
-// { dg-final { scan-assembler-not "weak\[^\n\r\]*variable" } }
+// { dg-final { scan-assembler "weak\[^\n\r\]*variableMUlvE_clEv" { target c++14_down } } }
+// in c++17 and up, this operator() become constexpr, no not emitted
+// { dg-final { scan-assembler-not "weak\[^\n\r\]*variableMUlvE_clEv" { target c++17 } } }
 int variable = []{return 1;}();
 
 // And a template instantiated with such a lambda is also internal.
diff --git c/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-1.C w/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-1.C
new file mode 100644
index 00000000000..ae7cbf03091
--- /dev/null
+++ w/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-1.C
@@ -0,0 +1,17 @@ 
+// { dg-do compile { target c++14 } }
+// PR 94426 ICE mangling lambda
+// { dg-options {-flto -O2} }
+
+template <bool> using Void = void;
+
+template <typename U> bool Init (U) {return true;}
+template <typename> bool VAR = Init ([] {});
+
+template <typename T>
+Void<false && VAR<T>> Foo (T)
+{}
+
+void q ()
+{
+  Foo ([] {});
+}
diff --git c/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C w/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
new file mode 100644
index 00000000000..3db864c604b
--- /dev/null
+++ w/gcc/testsuite/g++.dg/cpp0x/lambda/pr94426-2.C
@@ -0,0 +1,20 @@ 
+// { dg-do compile { target c++14 } }
+// PR 94426 ICE mangling lambda
+
+template <bool> using Void = void;
+
+template <typename U> bool Init (U) {return true;}
+template <typename> bool VAR = Init ([] {});
+
+template <typename T>
+Void<false && VAR<T>> Foo (T)
+{}
+
+void q ()
+{
+  Foo ([] {});
+}
+
+// The instantiation of VAR becomes local
+// { dg-final { scan-assembler {.local	_Z3VARIZ1qvEUlvE_E} { target { i?86-*-* x86_64-*-* } } } }
+// { dg-final { scan-assembler {.comm	_Z3VARIZ1qvEUlvE_E,1,1} { target { i?86-*-* x86_64-*-* } } } }
diff --git c/gcc/testsuite/g++.dg/opt/dump1.C w/gcc/testsuite/g++.dg/opt/dump1.C
index f813044456c..38ed055d5c6 100644
--- c/gcc/testsuite/g++.dg/opt/dump1.C
+++ w/gcc/testsuite/g++.dg/opt/dump1.C
@@ -312,7 +312,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
     typename __add_ref<
                       typename tuple_element<__i, tuple<_Elements...>>::type
                     >::type
-    get(tuple<_Elements...>& __t) noexcept; // { dg-warning "used but never defined" }
+    get(tuple<_Elements...>& __t) noexcept;
   template<std::size_t... _Indexes>
     struct _Index_tuple
     {};
@@ -387,7 +387,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
     };
   template<typename _Callable, typename... _Args>
     typename _Bind_simple_helper<_Callable, _Args...>::__type
-    __bind_simple(_Callable&& __callable, _Args&&... __args)  // { dg-warning "used but never defined" }
+    __bind_simple(_Callable&& __callable, _Args&&... __args) 
   ;
   union _Any_data
   ;
@@ -404,7 +404,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
       {
       protected:
  static _Functor*
- _M_get_pointer(const _Any_data& __source)  // { dg-warning "used but never defined" }
+ _M_get_pointer(const _Any_data& __source)
  ;
       };
   };
@@ -511,7 +511,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
         _S_construct(_Alloc&, _Tp* __p, _Args&&... __args)
  { ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); }
       static pointer
-      allocate(_Alloc& __a, size_type __n)  // { dg-warning "used but never defined" }
+      allocate(_Alloc& __a, size_type __n) 
       ;
       template<typename _Tp, typename... _Args>
  static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args)