llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.25k stars 12.08k forks source link

What causes clang to produce weak external symbols for template parameter objects? #113824

Open weinig opened 3 weeks ago

weinig commented 3 weeks ago

I'm not 100% confident that this is clang, it might be the linkers doing, but I'm trying to figure out what the criteria is for when a symbol with the form "weak external template parameter object" is produced and if that criteria has changed over releases.

Background is that I'm attempting to use NTTPs somewhat extensively in a project (WebKit) that has a post build script that looks for symbols in the linked output (via nm -m) that are "weak external" (https://github.com/WebKit/WebKit/blob/main/Tools/Scripts/check-for-weak-vtables-and-externals#L80) and errors if it finds any. (I don't know the history behind the script.)

I have gotten a report from the WebKit team that on older versions of clang (I am working on getting better details from them, but so far I have "Debug Sonoma and Ventura Xcode"), this script is producing an error.

I haven't been able to figure out what exactly provokes the generation of this type of symbol and was hoping there might be something I am just overlooking. My presumption is that it has to do with some ODR, but I can't work out any details.

--

In case it is helpful, the specific symbols it generates weak external for are:

ZTAXtlN7WebCore3CSS5RangeELd0000000000000000ELd7ff0000000000000EEE (run through c++filt: "template parameter object for WebCore::CSS::Range{0x0p+0, inf}") ZTAXtlN7WebCore3CSS5RangeELdfff0000000000000ELd7ff0000000000000EEE (run through c++filt: "template parameter object for WebCore::CSS::Range{-inf, inf}")

and the commit it triggers on is https://github.com/WebKit/WebKit/commit/4692eacc698b91686bf635d0b4e3030853edfd97

keinflue commented 3 weeks ago

FYI: In C++20 class-type NTTPs were introduced. For each set of equivalent template arguments for such a class-type NTTPs there exists one static storage duration object of the class type which any use of the template parameter in the template refers to as lvalue. This is the template parameter object. It doesn't exist for non-class NTTPs where the name of the template parameter is instead considered a prvalue with the template argument's value.

weinig commented 3 weeks ago

Ah, two things that might be helpful, the code is being compiled with c++2b, and the type being used is:

struct Range {
    // Convenience to allow for a shorter spelling of the appropriate infinity.
    static constexpr auto infinity = std::numeric_limits<double>::infinity();

    double min { -infinity };
    double max {  infinity };

    constexpr bool operator==(const Range&) const = default;
};

inline constexpr auto All = Range { -Range::infinity, Range::infinity };
inline constexpr auto Nonnegative = Range { 0, Range::infinity };

usually in a form like:

template<Range R = All> struct Length {
    static constexpr auto range = R;
    float value;
};

...

template<auto R> void foo(Length<R> length)
{
    ...
}

// or

void foo(Length<All> length)
{
    ...
}

(though there is some more complex usage of Range such as one place where we compile time merge them)


// Merges the two ranges, `aR` and `bR`, creating a union of their ranges.
consteval Range mergeRanges(Range aR, Range bR)
{
    return Range { std::min(aR.min, bR.min), std::max(aR.max, bR.max) };
}

// Convert to `calc(100% - (a + b))`.
//
// Returns a LengthPercentage with range, `resultR`, equal to union of the two input ranges `aR` and `bR`.
template<auto aR, auto bR> auto reflectSum(const LengthPercentage<aR>& a, const LengthPercentage<bR>& b) -> LengthPercentage<mergeRanges(aR, bR)>
{
    constexpr auto resultR = mergeRanges(aR, bR);

   ...

    return LengthPercentage< resultR>(...);
}
weinig commented 3 weeks ago

(also, I would normally try to find a minimal case, but since I don't have access to the builders where the issue is happening I am hoping by understanding what might cause the generation of this symbol type can point me to an answer (or decision to just allow this case)).