extern array as non-type template argument with clang c++1z ?

Dear clang folks,

Does C++17 allow using extern array as non-type template argument?

Clang gives error when compiling follow code: Compiler Explorer

template <const char* T>
struct X {
};

extern const char tag;
typedef X<tag> Y;

When compiling with clang from trunk @311886:

$ clang++ nontype.cc -std=c++1z
nontype.cc:6:11: error: non-type template argument refers to subobject '&tag'
typedef X<tag> Y;
          ^
1 error generated.

I found a similar question on SO: c++ - extern array as non-type template argument with clang c++1z - Stack Overflow

I traced the code a little bit, the error was emitted by
Sema::CheckTemplateArgument() at

Where we have Value.hasLValuePath() == false for extern array.

The code works fine in C++98/11/14 mode with clang, as well as
-std=c++17 with g++.

I also tried using a non-extern array:

const char tag = "abc";
struct Z {
  X<tag> x_;
};

But g++ (6/7) complains with "warning: 'Z' has a field 'Z::x_' whose
type uses the anonymous namespace [-Wsubobject-linkage]"

See https://gist.github.com/chenshuo/ed35a5615d6696be4b65a74515d598e2,
this warning only appears when array 'tag' is in defined in header
file. So I could not post a link to online compiler here.

Finally, I tried extern array with initialization:

extern const char tag = "abc";

Both clang and g++ compile fine, but I got linker error of duplicated
symbol: multiple definition of `tag'.

What is the correct way of using array as non-type template argument
in C++17? Thanks in advance!

Best Regards,
Shuo Chen
http://github.com/chenshuo

Dear clang folks,

Does C++17 allow using extern array as non-type template argument?

Clang gives error when compiling follow code: Compiler Explorer

template <const char* T>
struct X {
};

extern const char tag;
typedef X<tag> Y;

When compiling with clang from trunk @311886:

$ clang++ nontype.cc -std=c++1z
nontype.cc:6:11: error: non-type template argument refers to subobject
'&tag'
typedef X<tag> Y;
          ^
1 error generated.

This is "correct" behavior in the sense that Clang is following the
standard's rules, but is a bug in the standard wording. (You have my
apologies, this was my fault...) There is a proposed resolution for this
standard bug:

http://open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#2043

... and clang even implements that resolution, but it doesn't fire for your
testcase.

The problem turns out to be that clang's constant expression evaluation
support does not properly handle array-to-pointer decay on incomplete array
types. The rules for such cases are unclear, which is another open bug in
the standard; I've implemented the most likely resolution for that standard
issue in r311970, and your testcase is now accepted by Clang trunk.

Thanks for the report!

I found a similar question on SO: https://stackoverflow.com/
questions/38147062

I traced the code a little bit, the error was emitted by
Sema::CheckTemplateArgument() at
https://github.com/llvm-mirror/clang/blob/0a0eebabaad89213cd79fe9947fbb4
49484b6efd/lib/Sema/SemaTemplate.cpp#L6054
Where we have Value.hasLValuePath() == false for extern array.

The code works fine in C++98/11/14 mode with clang, as well as
-std=c++17 with g++.

I also tried using a non-extern array:

const char tag = "abc";
struct Z {
  X<tag> x_;
};

But g++ (6/7) complains with "warning: 'Z' has a field 'Z::x_' whose
type uses the anonymous namespace [-Wsubobject-linkage]"

See https://gist.github.com/chenshuo/ed35a5615d6696be4b65a74515d598e2,
this warning only appears when array 'tag' is in defined in header
file. So I could not post a link to online compiler here.

Finally, I tried extern array with initialization:

extern const char tag = "abc";

Both clang and g++ compile fine, but I got linker error of duplicated
symbol: multiple definition of `tag'.

What is the correct way of using array as non-type template argument
in C++17? Thanks in advance!

There are a couple of workarounds for this issue. If you specify the array
bound, Clang will accept:

extern const char tag[4];
typedef X<tag> Y;

Alternatively, you can declare the variable inline:

extern inline const char tag = "abc";

(You will need to make this change to every declaration of the variable.)

Hi Richard,

I've implemented the most likely resolution for that standard
issue in r311970, and your testcase is now accepted by Clang trunk.

Thanks for the quick turnaround.

There are a couple of workarounds for this issue. If you specify the array
bound, Clang will accept:

extern const char tag[4];
typedef X<tag> Y;

Thanks for your suggestion, it works perfectly in clang++ and g++.

Best Regards,
Shuo Chen