Skip to content

Clang reports an undefined function that has been defined #73232

@tbaederr

Description

@tbaederr
Contributor

Not sure how to title this properly.

The original reproducer is just:

#include <string>
constexpr auto z = std::string("", 0);

with libstdc++.

Using cvise, I get:

template <typename _CharT>
struct basic_string {
  constexpr void _M_construct();

  constexpr basic_string() {
    _M_construct();
  }

};


basic_string<char *> a;


template <typename _CharT>
constexpr void basic_string<_CharT>::_M_construct(){}

constexpr basic_string<char*> z{};

https://godbolt.org/z/7PzreExcf

Clang's output is:

<source>:18:16: error: constexpr variable 'z' must be initialized by a constant expression
   18 | constexpr auto z = basic_string<char *>();
      |                ^   ~~~~~~~~~~~~~~~~~~~~~~
<source>:6:5: note: undefined function '_M_construct' cannot be used in a constant expression
    6 |     _M_construct();
      |     ^
<source>:18:20: note: in call to 'basic_string()'
   18 | constexpr basic_string<char*> z{}
      |                    ^~~~~~~~~~~~~~~~~~~~~~
<source>:3:18: note: declared here
    3 |   constexpr void _M_construct();
      |                  ^

The problem vanishes if a is commented out, or if basic_string is not a template anymore.

Activity

added
clang:frontendLanguage frontend issues, e.g. anything involving "Sema"
libstdc++GNU libstdc++ C++ standard library
on Nov 23, 2023
llvmbot

llvmbot commented on Nov 23, 2023

@llvmbot
Member

@llvm/issue-subscribers-clang-frontend

Author: Timm Baeder (tbaederr)

Not sure how to title this properly.

The original reproducer is just:

#include &lt;string&gt;
constexpr auto z = std::string("", 0);

with libstdc++.

Using cvise, I get:

template &lt;typename _CharT&gt;
struct basic_string {
  constexpr void _M_construct();

  constexpr basic_string() {
    _M_construct();
  }

};

void operators() {
  basic_string&lt;char *&gt;{};
}

template &lt;typename _CharT&gt;
constexpr void basic_string&lt;_CharT&gt;::_M_construct(){}

constexpr basic_string&lt;char*&gt; z{};

https://godbolt.org/z/7PzreExcf

Clang's output is:

&lt;source&gt;:18:16: error: constexpr variable 'z' must be initialized by a constant expression
   18 | constexpr auto z = basic_string&lt;char *&gt;();
      |                ^   ~~~~~~~~~~~~~~~~~~~~~~
&lt;source&gt;:6:5: note: undefined function '_M_construct' cannot be used in a constant expression
    6 |     _M_construct();
      |     ^
&lt;source&gt;:18:20: note: in call to 'basic_string()'
   18 | constexpr auto z = basic_string&lt;char *&gt;();
      |                    ^~~~~~~~~~~~~~~~~~~~~~
&lt;source&gt;:3:18: note: declared here
    3 |   constexpr void _M_construct();
      |                  ^

The problem vanishes if operators() is commented out, or if basic_string is not a template anymore.

cor3ntin

cor3ntin commented on Nov 23, 2023

@cor3ntin
Contributor

same problem with free functions

template <typename T>
constexpr void g(T);

constexpr int f() {
    g(0);
    return 0;
}

template <typename T> 
constexpr void g(T) {}

constexpr auto z = f();
added
confirmedVerified by a second party
and removed
libstdc++GNU libstdc++ C++ standard library
on Nov 23, 2023
cor3ntin

cor3ntin commented on Nov 23, 2023

@cor3ntin
Contributor

We probably want to call Sema::InstantiateFunctionDefinition in CheckConstexprFunction.
Unfortunately, CheckConstexprFunction does not have access to Sema. in general, nothing in ExprConstant has access to Sema.
To fix that we'd need to add a reference to Sema in EvalInfo, probably. A bit tedious. Anyone has a better idea? @erichkeane

tbaederr

tbaederr commented on Nov 23, 2023

@tbaederr
ContributorAuthor

CC @AaronBallman and @zygoloid, maybe they have an idea

zygoloid

zygoloid commented on Nov 23, 2023

@zygoloid
Collaborator

Ultimately this is a language bug. There's no point of instantiation after the function template is defined and before the end of the TU -- we only get PoIs at uses and at end of TU.

What we should probably do is instantiate all used specializations at the point where the function template is defined (if it's constexpr). Constant evaluation shouldn't have side effects.

I'm pretty sure this is a duplicate of a (very) old PR.

zygoloid

zygoloid commented on Nov 23, 2023

@zygoloid
Collaborator

See CWG2497.

cor3ntin

cor3ntin commented on Nov 24, 2023

@cor3ntin
Contributor

Ultimately this is a language bug. There's no point of instantiation after the function template is defined and before the end of the TU -- we only get PoIs at uses and at end of TU.

What we should probably do is instantiate all used specializations at the point where the function template is defined (if it's constexpr). Constant evaluation shouldn't have side effects.

Hum, I can see how trying to do instantiations in the middle of constant evaluation would not be great, thanks!

Do you think we should wait for core to resolve the issue, or should we try to eagerly instantiate at the end of the definition of constexpr functions?

zygoloid

zygoloid commented on Nov 25, 2023

@zygoloid
Collaborator

I think we should eagerly start instantiating at the end of definitions. (Both function and variable templates are affected by this IIRC.)

self-assigned this
on Nov 26, 2023

10 remaining items

zygoloid

zygoloid commented on Dec 2, 2023

@zygoloid
Collaborator

Normally I'd just say that the code needs to be fixed to give the explicit specialization before it's used or to make sure the instantiation is deferred, but the breakage is probably too much given that this seems to be in Qt core code.

I guess the question is, do we follow GCC's problematic model that evaluation can cause instantiation (which the language rules elsewhere explicitly try to avoid), or do we add a workaround just for Qt (detect that pattern somehow and don't eagerly Instantiate)?

cor3ntin

cor3ntin commented on Dec 3, 2023

@cor3ntin
Contributor

I guess the question is, do we follow GCC's problematic model that evaluation can cause instantiation (which the language rules elsewhere explicitly try to avoid), or do we add a workaround just for Qt (detect that pattern somehow and don't eagerly Instantiate)?

It seems like EDG/MSVC follow the same model. I sent a mail to CWG, but i doubt it will help make progress.

cor3ntin

cor3ntin commented on Apr 8, 2024

@cor3ntin
Contributor

@zygoloid The current direction taking by the reflection proposal is that arbitrary instantiations can emanate from constexpr evaluation. (Both explicitly as there is a proposed method to instantiate and define classes, and implicitly when querying the properties of not yet instantiated definitions).

In that way the model you consider problematic is going to be mandated by the standard

Is that something you gave any thought above? It might impact how we resolve this issue

zygoloid

zygoloid commented on Apr 8, 2024

@zygoloid
Collaborator

Thinking about the Qt example some more, if you simply reverse the order of the function definitions:

template <typename> constexpr static void fromType();

template <typename> struct QMetaTypeId  {};
template <typename T> constexpr void fromType() { 
  (void)QMetaTypeId<T>{};
}

void registerConverter() { fromType<int>(); }

template <> struct QMetaTypeId<int> {};

... then the example is clearly invalid (albeit no diagnostic required, because there are two points of instantiation for fromType<int> and only one of them results in the use of QMetaTypeId<int> prior to the explicit specialization), and clang correctly rejects. (GCC still doesn't, which is also permissible.) It seems pretty hard to justify why this example should be valid with the use and the template definition in the other order. The explicit specialization is simply declared too late.

Fedr

Fedr commented on Apr 7, 2025

@Fedr

A similar issue presented in https://stackoverflow.com/q/79558008/7325599

struct A {
    consteval A(int i) {
        chk(i);
    }
    constexpr void chk(auto) {}
};

A a{1};

GCC, EDG, MSVC all accept it. But Clang complains:

<source>:8:3: error: call to consteval function 'A::A' is not a constant expression
    8 | A a{1};
      |   ^
<source>:3:9: note: undefined function 'chk<int>' cannot be used in a constant expression
    3 |         chk(i);
      |         ^
<source>:8:3: note: in call to 'A(1)'
    8 | A a{1};
      |   ^~~~
<source>:5:20: note: declared here
    5 |     constexpr void chk(auto) {}
      |                    ^

Online demo: https://gcc.godbolt.org/z/xnGEM7oqe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

clang:frontendLanguage frontend issues, e.g. anything involving "Sema"confirmedVerified by a second partylibstdc++GNU libstdc++ C++ standard libraryrejects-valid

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Participants

    @zygoloid@cor3ntin@Fedr@Endilll@frederick-vs-ja

    Issue actions

      Clang reports an undefined function that has been defined · Issue #73232 · llvm/llvm-project