Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions src/c_gen_be.c
Original file line number Diff line number Diff line change
Expand Up @@ -10851,12 +10851,19 @@ if this routine has a body (dump nothing if it has no body).
a_boolean is_marked_weak = FALSE;
a_boolean thread_local_init_case = FALSE;
a_boolean for_inlining_only;
a_boolean inline_only_defn = FALSE;
a_boolean need_gnu_inline_attr = FALSE;

if (c99_mode && rout->definition_for_inlining_only &&
!gcc_version_is(< 50000)) {
!gcc_version_is(< 50000)
#if GNU_EXTENSIONS_ALLOWED
&& !rout->gnu_c89_inline
#endif /* GNU_EXTENSIONS_ALLOWED */
) {
/* This is a C99 "inline definition", so we must suppress the "extern"
keyword to avoid turning it into an external definition. (gcc did
not support C99 inline semantics until version 5.1.0.) */
not support C99 inline semantics until version 5.1.0. A routine with
the gnu_inline attribute follows the GNU C89 rules instead.) */
for_inlining_only = TRUE;
} else {
for_inlining_only = FALSE;
Expand All @@ -10881,8 +10888,23 @@ if this routine has a body (dump nothing if it has no body).
provided only for the purpose of inlining -- "extern inline".
Put out the definition in that case. */
has_defn = TRUE;
inline_only_defn = TRUE;
if (!for_inlining_only) {
storage_class = (a_storage_class)sc_extern;
/* "extern inline" is a definition only for inlining under the GNU
C89 rules, but an external definition under the C99 rules (the
gcc default since 5.1), so request the former with the gnu_inline
attribute. */
need_gnu_inline_attr = TRUE;
#if GCC_IS_GENERATED_CODE_TARGET
if (gcc_is_generated_code_target &&
gnu_target_version_number < 40200) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above comment mentions gcc 5.1, but this tests for 4.2; it would be good to clarify what's going on (or make the test relative to 5.1).

need_gnu_inline_attr = FALSE;
} /* if */
#endif /* GCC_IS_GENERATED_CODE_TARGET */
#if GNU_EXTENSIONS_ALLOWED
if (rout->gnu_c89_inline) need_gnu_inline_attr = FALSE;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably if this is true the gnu_inline is added outside of this code path; it might be worth adding a comment to clarify why this is happening.

Something like:

if (rout->gnu_c89_inline) {
  /* The routine already will have a __gnu__inline__ attribute. */
  need_gnu_inline_attr = FALSE;
}  /* if */

#endif /* GNU_EXTENSIONS_ALLOWED */
} /* if */
} /* if */
} /* if */
Expand Down Expand Up @@ -11143,15 +11165,18 @@ if this routine has a body (dump nothing if it has no body).
#endif /* MICROSOFT_EXTENSIONS_ALLOWED */
if (gcc_or_clang_is_generated_code_target && rout->is_inline) {
/* gcc will be used to compile this generated code, so we know how to
indicate an inline function. */
/* gcc ignores __inline__ on functions with ellipses, so don't
mark such functions as inline. Recent versions of gcc issue an
error for a function marked both inline and weak, so don't mark a
weak function as inline, either. */
if (!f_skip_typerefs(rout->type)->variant.routine.extra_info->
has_ellipsis &&
indicate an inline function. gcc ignores __inline__ on functions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space, should be function. gcc.

with ellipses, so don't mark such functions as inline. Also,
recent versions of gcc issue an error for a function marked both
inline and weak, so don't mark a weak function as inline either */
if ((!f_skip_typerefs(rout->type)->variant.routine.extra_info->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be rout_type_supp(f_skip_typerefs(rout->type)).

has_ellipsis ||
inline_only_defn) &&
!is_marked_weak) {
write_tok_str("__inline__ ");
if (need_gnu_inline_attr) {
write_tok_str("__attribute__((__gnu_inline__)) ");
} /* if */
} /* if */
} /* if */
#if GNU_EXTENSIONS_ALLOWED && !LOWER_IFUNC
Expand Down
38 changes: 38 additions & 0 deletions src/pending_changes/gh-252-extern-inline-definitions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[GH #252]
C-generating back end: inline-only definitions emitted as external ones

When an inline function definition was to be used only for inlining (for
example, a member of a class template specialization that is the subject of
an explicit instantiation declaration), the C-generating back end emitted it
as an "extern __inline__" definition for GCC and Clang. That form is a
definition only for inlining under the GNU C89 rules, but an external
definition under the C99 rules that GCC uses by default since version 5.
Every object file that used such a function then contained an external
definition of it, and linking two such object files failed. For example,
with --c++17:

// a.h
template <class T> struct A {
int f() { return 1; }
};
extern template struct A<int>;

// a.cpp
#include "a.h"
int g1() { return A<int>().f(); }

// b.cpp
#include "a.h"
int g2() { return A<int>().f(); } // Linking with a.cpp previously failed.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// c.cpp
#include "a.h"
template struct A<int>;

The same problem affected GNU C89 "extern inline" functions, functions with
the gnu_inline attribute in C99 and later C modes (which lost the "extern"
keyword), and such definitions of functions with an ellipsis (which were not
marked inline). This is now fixed: The C-generating back end now adds the
__gnu_inline__ attribute to "extern __inline__" definitions, keeps "extern"
for functions with the gnu_inline attribute, and marks a definition only for
inlining as inline even when the function has an ellipsis.
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -4211,7 +4211,6 @@ gnu/c/torture/pr68661-1a.sft.c:s:-DTEST_NUMBER=1:SKIPPED:
gnu/c/torture/pr68661-1b.sft.c:s:-DTEST_NUMBER=1:SKIPPED:
gnu/c/torture/pr69242.sft.c:fp:-DTEST_NUMBER=1 -w:FAIL:
gnu/c/torture/pr69886.sft.c:s:-DTEST_NUMBER=1 -w:SKIPPED:
gnu/c/torture/pr70123.sft.c:rp:-DTEST_NUMBER=1:BADC(COMPILE):
gnu/c/torture/pr70457.sft.c:fp:-DTEST_NUMBER=1:FAIL:
gnu/c/torture/pr70985.sft.c:fp:-DTEST_NUMBER=1:FAIL:
gnu/c/torture/pr71594.sft.c:s:-DTEST_NUMBER=1:SKIPPED:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,3 @@ eccp -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnostics
__attribute__ ((noinline, noclone)) int
^

eccp: diagnostics generated from compilation of Test_name.int.c:
Test_name.c: In function 'baz':
Test_name.c:15:8: error: invalid use of '__builtin_va_arg_pack ()'
eccp: end of diagnostics from compilation of Test_name.int.c
eccp: gcc compilation of Test_name.int.c returned an exit status of 1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnostics --gcc Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
Test_name.c: In function 'baz':
Test_name.c:15:8: error: invalid use of '__builtin_va_arg_pack ()'
eccp: end of diagnostics from compilation of Test_name.int.c
eccp: gcc compilation of Test_name.int.c returned an exit status of 1
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnostics --gcc Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
Test_name.c: In function 'baz':
Test_name.c:15:8: error: invalid use of '__builtin_va_arg_pack ()'
eccp: end of diagnostics from compilation of Test_name.int.c
eccp: gcc compilation of Test_name.int.c returned an exit status of 1
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-1.C: In function '_Z3bariz':
./ext/va-arg-pack-1.C:127:108: error: invalid use of '__builtin_va_arg_pack ()'
./ext/va-arg-pack-1.C:128:114: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z3bariz',
inlined from 'main' at ./ext/va-arg-pack-1.C:134:103:
./ext/va-arg-pack-1.C:127:108: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-1.C: In function '_Z3bariz':
./ext/va-arg-pack-1.C:127:93: error: invalid use of '__builtin_va_arg_pack ()'
./ext/va-arg-pack-1.C:128:99: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z3bariz',
inlined from 'main' at ./ext/va-arg-pack-1.C:134:88:
./ext/va-arg-pack-1.C:127:93: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-1.C: In function '_Z3bariz':
./ext/va-arg-pack-1.C:127:92: error: invalid use of '__builtin_va_arg_pack ()'
./ext/va-arg-pack-1.C:128:98: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z3bariz',
inlined from 'main' at ./ext/va-arg-pack-1.C:134:88:
./ext/va-arg-pack-1.C:127:92: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-1.C: In function '_Z3bariz':
./ext/va-arg-pack-1.C:127:107: error: invalid use of '__builtin_va_arg_pack ()'
./ext/va-arg-pack-1.C:128:113: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z3bariz',
inlined from 'main' at ./ext/va-arg-pack-1.C:134:103:
./ext/va-arg-pack-1.C:127:107: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-len-1.C: In function '_Z6myopenPKciz':
./ext/va-arg-pack-len-1.C:81:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:94:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:97:113: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z6myopenPKciz',
inlined from 'main' at ./ext/va-arg-pack-len-1.C:106:5:
./ext/va-arg-pack-len-1.C:97:113: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-len-1.C: In function '_Z6myopenPKciz':
./ext/va-arg-pack-len-1.C:81:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:94:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:97:98: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z6myopenPKciz',
inlined from 'main' at ./ext/va-arg-pack-len-1.C:106:5:
./ext/va-arg-pack-len-1.C:97:98: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-len-1.C: In function '_Z6myopenPKciz':
./ext/va-arg-pack-len-1.C:81:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:94:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:97:96: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z6myopenPKciz',
inlined from 'main' at ./ext/va-arg-pack-len-1.C:106:5:
./ext/va-arg-pack-len-1.C:97:96: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
eccp -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics Test_name.c
eccp: diagnostics generated from compilation of Test_name.int.c:
./ext/va-arg-pack-len-1.C: In function '_Z6myopenPKciz':
./ext/va-arg-pack-len-1.C:81:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:94:6: error: invalid use of '__builtin_va_arg_pack_len ()'
./ext/va-arg-pack-len-1.C:97:111: error: invalid use of '__builtin_va_arg_pack ()'
In function '_Z6myopenPKciz',
inlined from 'main' at ./ext/va-arg-pack-len-1.C:106:5:
./ext/va-arg-pack-len-1.C:97:111: error: invalid use of '__builtin_va_arg_pack ()'
Expand Down
4 changes: 4 additions & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
eccp -DTEST_NUMBER=1 --c11 Test_name.c use.c def.c
Test_name.c:
use.c:
def.c:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
4 changes: 4 additions & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
eccp -DTEST_NUMBER=2 --c11 --gnu=150100 Test_name.c use.c def.c
Test_name.c:
use.c:
def.c:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.2.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
4 changes: 4 additions & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
eccp -DTEST_NUMBER=3 --c --gnu=40200 Test_name.c use.c def.c
Test_name.c:
use.c:
def.c:
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252-c/.test.rto/default.3.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
3 changes: 3 additions & 0 deletions tests/tests/regressions/gh-252-c/def.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int h(void) { return 1; }
int v(int n, ...) { return n; }
int k(void) { return 1; }
13 changes: 13 additions & 0 deletions tests/tests/regressions/gh-252-c/inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* Definitions only for inlining. */
#if __GNUC_GNU_INLINE__
#define INLINE_ONLY extern inline
#else
#define INLINE_ONLY inline
#endif
INLINE_ONLY int h(void) { return 1; }
INLINE_ONLY int v(int n, ...) { return n; }
#ifdef __GNUC__
extern inline __attribute__((gnu_inline)) int k(void) { return 1; }
#else
INLINE_ONLY int k(void) { return 1; }
#endif
11 changes: 11 additions & 0 deletions tests/tests/regressions/gh-252-c/test.mft.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//type:rp
//options:--c11:--c11 --gnu=150100:--c --gnu=40200
//source_files:inl.h use.c def.c
/* h, v, and k are used here and in use.c, but only def.c may define them.
Each object used to contain an external definition of v, of h (with the
GNU C89 inline rules), or of k (with the C99 inline rules). */
#include "inl.h"
int use(void);
int main(void) {
return (h() == 1 && v(1, 2) == 1 && k() == 1 && use() == 3) ? 0 : 1;
}
2 changes: 2 additions & 0 deletions tests/tests/regressions/gh-252-c/use.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "inl.h"
int use(void) { return h() + v(1, 3) + k(); }
7 changes: 7 additions & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
./check_defs -DTEST_NUMBER=1 --c++17 Test_name.c use.C inst.C
Test_name.c:
use.C:
inst.C:
Test_name.o:
use.o:
inst.o: _ZN1AIiE1fEv _ZN1AIiE1gEiz
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
7 changes: 7 additions & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
./check_defs -DTEST_NUMBER=2 --c++17 --g++ Test_name.c use.C inst.C
Test_name.c:
use.C:
inst.C:
Test_name.o:
use.o:
inst.o: _ZN1AIiE1fEv _ZN1AIiE1gEiz
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.2.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
7 changes: 7 additions & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
./check_defs -DTEST_NUMBER=3 --c++17 --g++ -tused Test_name.c use.C inst.C
Test_name.c:
use.C:
inst.C:
Test_name.o:
use.o:
inst.o: _ZN1AIiE1fEv _ZN1AIiE1gEiz
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.3.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
7 changes: 7 additions & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.4.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
./check_defs -DTEST_NUMBER=4 --c++17 --clang Test_name.c use.C inst.C
Test_name.c:
use.C:
inst.C:
Test_name.o:
use.o:
inst.o: _ZN1AIiE1fEv _ZN1AIiE1gEiz
1 change: 1 addition & 0 deletions tests/tests/regressions/gh-252/.test.rto/default.4.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
5 changes: 5 additions & 0 deletions tests/tests/regressions/gh-252/a.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
template <class T> struct A {
int f() { return 1; }
int g(int n, ...) { return n; }
};
extern template struct A<int>;
31 changes: 31 additions & 0 deletions tests/tests/regressions/gh-252/check_defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
# Compile each source file to an object file, check that only inst.o (the
# explicit instantiation) defines A<int>::f and A<int>::g, and then link the
# object files.
opts=
objs=
for arg in "$@"; do
case $arg in
*.c | *.C) objs="$objs ${arg%.*}.o" ;;
*) opts="$opts $arg" ;;
esac
done
eccp -c "$@" || exit $?
status=0
for obj in $objs; do
defs=`nm -g --defined-only $obj | grep -o '_ZN1AIiE1[fg]E[a-z]*' | sort`
defs=`echo $defs`
echo $obj: $defs
expected=
if [ $obj = inst.o ]; then
expected="_ZN1AIiE1fEv _ZN1AIiE1gEiz"
fi
if [ "$defs" != "$expected" ]; then
echo "FAIL: Expected '$expected'."
status=1
fi
done
if [ $status -ne 0 ]; then
exit $status
fi
exec eccp $opts $objs
2 changes: 2 additions & 0 deletions tests/tests/regressions/gh-252/inst.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "a.h"
template struct A<int>;
11 changes: 11 additions & 0 deletions tests/tests/regressions/gh-252/test.mft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//type:rp
//options:--c++17:--c++17 --g++:--c++17 --g++ -tused:--c++17 --clang
//source_files:a.h use.C inst.C
//script:./check_defs
// The members of A<int> are used here and in use.C, but only inst.C may
// define them; each object used to contain an external definition.
#include "a.h"
int use();
int main() {
return (A<int>().f() == 1 && A<int>().g(1, 2) == 1 && use() == 2) ? 0 : 1;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
2 changes: 2 additions & 0 deletions tests/tests/regressions/gh-252/use.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "a.h"
int use() { return A<int>().f() + A<int>().g(1, 3); }
Loading