Нарушение ODR на концептах
От: Кодт Россия  
Дата: 28.05.21 20:32
Оценка: 49 (5)
Код для поиграться: https://gcc.godbolt.org/z/oeznrcGTa
clang и gcc ведут себя несколько по-разному, кстати.

Пусть у нас есть два концепта — Ptr и Vec (тип является голым указателем и вектором, соответственно).
Напишем три шаблона foo(const T&).

А потом попробуем вызвать для вектора / указателя на вектор / вектора векторов.
#include <iostream>
#include <vector>

template<class T> constexpr bool is_vector = false;
template<class T> constexpr bool is_vector<std::vector<T>> = true;
template<class T> concept Vec = is_vector<T>;

template<class T> constexpr bool is_pointer = false;
template<class T> constexpr bool is_pointer<T*> = true;
template<class T> concept Ptr = is_pointer<T>;

template<class T> void foo(T  ) { std::cout << "def) " << __PRETTY_FUNCTION__ << std::endl;            }
template<Ptr   T> void foo(T t) { std::cout << "ptr) " << __PRETTY_FUNCTION__ << std::endl; foo(*t);   }
template<Vec   T> void foo(T t) { std::cout << "vec) " << __PRETTY_FUNCTION__ << std::endl; foo(t[0]); }

int main() {
    std::vector v { 1 };

    auto pv = &v;
    auto ppv = &pv;

    std::vector vv {{ v }};
    std::vector vvv {{ vv }};

    foo(ppv);
    std::cout << std::endl;
    
    foo(v);
    std::cout << std::endl;

    foo(vvv);
}

В зависимости от того, что именно и в каком порядке мы напишем в main, получим разные результаты — включая ошибку линковки!

В чём тут проблема: определение foo<Ptr> видит только объявления foo<class> и само себя, естественно, — а foo<Vec> ещё не видит.
Поэтому foo(vector<int>) оказывается неоднозначным.

С одной стороны, компилятор инстанцирует его из foo(vector<int>*) как foo<class=vector<int>>;
с другой стороны, из main и/или из foo<Vec> — как foo<Vec=vector<int>>.

Ограничения не входят в манглированное имя (мы можем объявить параметр как концепт, или можем выписать requires, — эффект будет тот же).
Поэтому компилятор не различает эти функции, и мы отхватываем нарушение ODR.
Он или берёт ранее инстанцированную функцию из своего кеша, или принудительно создаёт ещё один объект в объектном файле, удивляя линкера. А при оптимизациях, подозреваю, начнёт хаотически инлайнить / вызывать что бог на душу положит.

Как исправить программу — очевидно. Надо выписать объявления до определений. (Минимально достаточно в данном случае вытащить третье объявление перед вторым определением).
Или, как вариант, запихнуть шаблоны функций в class scope, — там правила видимости другие, все видят всех сразу.

Вопрос в другом.

ЧТО ЭТО?
— дефект стандарта
— дефект правил манглирования имён
— или тут по стандарту должно было возникнуть ODR, и спрос исключительно с клиентского кода?
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Отредактировано 28.05.2021 20:35 Кодт . Предыдущая версия .
Re: Нарушение ODR на концептах
От: vopl Россия  
Дата: 29.05.21 12:03
Оценка: 50 (3)
Здравствуйте, Кодт, Вы писали:

К>Код для поиграться: https://gcc.godbolt.org/z/oeznrcGTa

К>clang и gcc ведут себя несколько по-разному, кстати.

Посмотрим что скажут товарищи из gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100825
Re: Нарушение ODR на концептах
От: vopl Россия  
Дата: 29.05.21 09:49
Оценка: 73 (2)
Здравствуйте, Кодт, Вы писали:

К>ЧТО ЭТО?

К>- дефект стандарта

Вроде как бы нет.. Если это переписать на trailing requires-clauses
  примерно так
#include <vector>

template<class T> constexpr bool is_vector = false;
template<class T> constexpr bool is_vector<std::vector<T>> = true;
template<class T> concept Vec = is_vector<T>;

template<class T> constexpr bool is_pointer = false;
template<class T> constexpr bool is_pointer<T*> = true;
template<class T> concept Ptr = is_pointer<T>;

template <class T> void foo(T t) {}
template <class T> void foo(T t) requires Ptr<T> {foo(*t);}
template <class T> void foo(T t) requires Vec<T> {foo(t[0]);}

int main() {
    std::vector v {1};

    foo(v);
    foo(&v);
}
проблема никуда не уходит, один к одному такая же. И тут можно явно применять следующее

12.3 Declaration matching [over.dcl]
1 Two function declarations of the same name refer to the same function if they are in the same scope and
have equivalent parameter declarations (12.2) and equivalent (13.7.6.1) trailing requires-clauses, if any (9.3).

то есть, конфликта быть не должно потому что разные trailing requires-clauses. Думаю, что в случае концептов в заголовке шаблона — ситуация должна разруливаться точно так же. Наверное в последних редакциях стандартах уже так и написано, но лень искать..

К>- дефект правил манглирования имён

Однозначно да. Ограничения должны входить в имя, но не входят.

[добавлено позже] .. потому что манглированное имя производится из сигнатуры, а она определяется так

3.21 [defns.signature.templ]
signature
〈function template〉 name, parameter-type-list (9.3.3.5), enclosing namespace (if any), return type, templatehead, and trailing requires-clause (9.3) (if any)

Отредактировано 29.05.2021 10:04 vopl . Предыдущая версия .
Re: Нарушение ODR на концептах
От: Vamp Россия  
Дата: 28.05.21 21:02
Оценка: +1 :)
Здравствуйте, Кодт, Вы писали:


К>ЧТО ЭТО?

К>- дефект стандарта

Ну вот я глубоко уверен, что вся концепция концептов — это дефект стандарта.20ка вообще полна их. Модули, концепты, корутины, вот это все — типичные баги стандарта, как по мне.
Да здравствует мыло душистое и веревка пушистая.
Re[3]: Нарушение ODR на концептах
От: vopl Россия  
Дата: 31.05.21 11:09
Оценка: 72 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, vopl, Вы писали:


V>>Посмотрим что скажут товарищи из gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100825


К>А товарищам из clang'а зашлёшь багрепорт?


https://bugs.llvm.org/show_bug.cgi?id=50540
Re: Нарушение ODR на концептах
От: σ  
Дата: 29.05.21 10:58
Оценка: 9 (1)
К>ЧТО ЭТО?
К>- дефект стандарта
К>- дефект правил манглирования имён
К>- или тут по стандарту должно было возникнуть ODR, и спрос исключительно с клиентского кода?
Возможно, второе.
Re: Нарушение ODR на концептах
От: reversecode google
Дата: 29.05.21 05:24
Оценка: 3 (1)
msvc если кому интересно

>Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30129.3 for x64


>fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '??$foo@V?$vector@HV?$allocator@H@std@@@std@@@@YAXV?$vector@HV?$allocator@H@std@@@std@@@Z'


>void __cdecl foo<class std::vector<int,class std::allocator<int> > >(class std::vector<int,class std::allocator<int> >)
Re: Нарушение ODR на концептах
От: kov_serg Россия  
Дата: 28.05.21 21:02
Оценка:
Здравствуйте, Кодт, Вы писали:

К>ЧТО ЭТО?

К>- дефект стандарта
К>- дефект правил манглирования имён
К>- или тут по стандарту должно было возникнуть ODR, и спрос исключительно с клиентского кода?

То что манглирования имён имён не имеет информации об ограничениях на функцию косяк стандарта.
Но не переживайте думаю в стандарте просто допишут что такая ситуация UB.

ps: вспомниля анекдот

Мама с Вовочкой приходит к доктору:
-Доктор, вылечите моего ребенка!
Доктор:
-Раздевайтесь.
Мать:
-Но ведь не меня лечить нужно, а ребенка!
Доктор:
-Раздевайтесь! Чем такого лечить, проще нового сделать!

Re[2]: Нарушение ODR на концептах
От: Кодт Россия  
Дата: 29.05.21 08:54
Оценка:
Здравствуйте, reversecode, Вы писали:

R>msvc если кому интересно

>>fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '??$foo@V?$vector@HV?$allocator@H@std@@@std@@@@YAXV?$vector@HV?$allocator@H@std@@@std@@@Z'

гусь и шланг то же самое делают, на определённых сочетаниях вызовов.
Создают два одноимённых символа в объектнике.
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re: Нарушение ODR на концептах
От: rg45 СССР  
Дата: 29.05.21 19:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>В зависимости от того, что именно и в каком порядке мы напишем в main, получим разные результаты — включая ошибку линковки!


В принципе, это очень похоже на баг, который существовал когда-то в Visual C++ 6.0: параметр шаблона функции, не фигурирующий в типах формальных параметров, не участвовал в декорировании имен. И так же точно такая функция инстанцировалась только один раз с тем типом, с которым впервые использовалась. Я думаю, многие помнят еще, как приходилось добавлять фейковые параметры функций, чтобы обойти эту проблему.
--
Завтра сегодня будет вчера
Re[2]: Нарушение ODR на концептах
От: Кодт Россия  
Дата: 31.05.21 10:33
Оценка:
Здравствуйте, vopl, Вы писали:

К>>- дефект правил манглирования имён

V>Однозначно да. Ограничения должны входить в имя, но не входят.

V>[добавлено позже] .. потому что манглированное имя производится из сигнатуры, а она определяется так

V>

3.21 [defns.signature.templ]
V>signature
V>〈function template〉 name, parameter-type-list (9.3.3.5), enclosing namespace (if any), return type, templatehead, and trailing requires-clause (9.3) (if any)


Вот оно!
Сдаётся мне, и gcc и clang одинаково облажались, потому что этот пункт был дописан уже после того, как они внедрили концепты-ограничения как экспериментальную фичу.
Ну а дальше вся разница в их поведении связана с тем, что оно из-за этого бага неопределённое.
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[2]: Нарушение ODR на концептах
От: Кодт Россия  
Дата: 31.05.21 10:34
Оценка:
Здравствуйте, vopl, Вы писали:

V>Посмотрим что скажут товарищи из gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100825


А товарищам из clang'а зашлёшь багрепорт?
http://files.rsdn.org/4783/catsmiley.gif Перекуём баги на фичи!
Re[4]: Нарушение ODR на концептах
От: reversecode google
Дата: 31.05.21 11:30
Оценка:
о раз вы там уже зарегистрированы
не затруднит апнуть один пр ?
https://bugs.llvm.org/show_bug.cgi?id=49626
Re[2]: Нарушение ODR на концептах
От: vopl Россия  
Дата: 01.06.21 15:55
Оценка:
Здравствуйте, vopl, Вы писали:

V>Посмотрим что скажут товарищи из gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100825


Хм, говорят что такая программа якобы ill-formed. Но, чето как то за уши это все притягивают, не очень убедительно..
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.