MSVC 2013 - ICE
От: Кодт Россия  
Дата: 13.03.15 13:28
Оценка:
// прямые шаблоны typedef'ов
template<template<class>class F, class X> using apply = F<X>;
template<template<class>class F, class X, class Y> using apply2 = F<X>;

// старые добрые метафункции с зависимыми именами
template<template<class>class F> struct apply_t { template<class X> using fun = F<X>; };
template<template<class>class F, class Z> struct apply2_t { template<class X> using fun = F<X>; };
// вывернем аргументом наружу, мясом внутрь
template<class X> struct pass_t { template<template<class>class F> using fun = F<X>; };
// замаскируем метафункцию
template<class Z> struct mask_t { template<template<class>class F, class X> using fun = F<X>; };

// особенно вот эта, в стиле С++98 (только using на typedef переделать)
template<template<class>class F, class X> struct applied_t { using result = F<X>; };



template<class... Args> struct run;
template<class... Args> struct run<Args...> // это важно! сбоит только на специализации, пусть даже и совпадающей с генеральным шаблоном
{
    template<class X> using one = X;

    // любая из этих строк делает ICE
    template<class X> using ice1 = one<X>; // вот причина айса - все остальные случаи сводятся к нему

    template<class X> using ice2 = apply<one,X>;
    template<class X> using ice3 = typename apply_t  <one    >::template fun<X>; // apply_t не зависит от X
    template<class X> using ice4 = typename apply2_t <one,int>::template fun<X>; // apply2_t не зависит от X

    // а эти строчки - не делают
    template<class X> using yes1 = typename apply2_t <one,X**>::template fun<X>; // apply2_t зависит от X
    template<class X> using yes2 = typename pass_t<X>::template fun<one>;
    template<class X> using yes3 = typename mask_t<X**>::template fun<one,X>;

    template<class X> using yes4 = typename applied_t<one,X>::result;
};

VS2013 — 18.00.30723 — ICE
VS2015 — 19.00.22620 — компилируется (и даже делает то, что нужно)

Обратите внимание на ice3, ice4 и yes1. Выглядят почти одинаково, а выстреливают по-разному.

При том, что в VS2015 баг исправлен, — но вдруг кто-то пользуется VS2013 и страдает от айса в каких-то схожих местах.
Решение, как мы видим, — в том, чтобы параметризовать используемый шаблон класса-метафункции параметром using'а.

Компилятор при этом думает "а вдруг существует специализация метафункции?" и не пытается подставить её раньше времени.
Перекуём баги на фичи!
Re: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 13.03.15 17:26
Оценка:
Здравствуйте, Кодт, Вы писали:

template<class... Args> struct run;
template<class... Args> struct run<Args...> // это важно! сбоит только на специализации, пусть даже и совпадающей с генеральным шаблоном

Это некорректный код. См. абзац 14.5.4/9 стандарта C++03, абзац 14.5.5/8 стандартов C++11/14:

The argument list of the specialization shall not be identical to the implicit argument list of the primary template.


clang 3.5.0 и g++ 4.9.2 его не пропускают.

К>VS2013 — 18.00.30723 — ICE


Почему-то не удалось добиться появления ICE на VS2013. Не могли бы Вы привести код целиком?
Re[2]: VS bug: new int(1,2,"wtf")
От: Кодт Россия  
Дата: 13.03.15 17:47
Оценка:
Здравствуйте, Constructor, Вы писали:

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


C>
C>template<class... Args> struct run;
C>template<class... Args> struct run<Args...> // это важно! сбоит только на специализации, пусть даже и совпадающей с генеральным шаблоном
C>

C>Это некорректный код. См. абзац 14.5.4/9 стандарта C++03, абзац 14.5.5/8 стандартов C++11/14:

Это не имеет значения. Можно любую специализацию, сколь угодно валидную. Лишь бы с вариадиком.
Например,
template<class... Args> struct run<int,Args...> {


C>clang 3.5.0 и g++ 4.9.2 его не пропускают.

Тем более, компилятор мог бы выдать диагностику.

К>>VS2013 — 18.00.30723 — ICE


C>Почему-то не удалось добиться появления ICE на VS2013. Не могли бы Вы привести код целиком?


Это весь код целиком.
Чтобы накренилось, нужно оставить любую строчку using iceN

Платформо-зависимых типов здесь нет, поэтому выбор платформы (x86, x64) не влияет.
Версию компилятора я указал.
Перекуём баги на фичи!
Re[3]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 13.03.15 18:21
Оценка:
Здравствуйте, Кодт, Вы писали:

C>>Почему-то не удалось добиться появления ICE на VS2013. Не могли бы Вы привести код целиком?


К>Это весь код целиком.

К>Чтобы накренилось, нужно оставить любую строчку using iceN

К>Платформо-зависимых типов здесь нет, поэтому выбор платформы (x86, x64) не влияет.

К>Версию компилятора я указал.

Это очень странно, потому что на VS 2013 w/o updates (cl version 18.0.21005.1) и на VS 2013 Update 4 (cl version 18.00.31101) никакого ICE не наблюдается, компилятор ругается только на отсутствие main. А у Вас, насколько я понимаю, VS 2013 Update 3.
Re: MSVC 2013 - ICE
От: swingus  
Дата: 13.03.15 22:42
Оценка:
что такое "исе"?

Здравствуйте, Кодт, Вы писали:
Re[2]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 13.03.15 22:47
Оценка: 1 (1)
Здравствуйте, swingus, Вы писали:

S>что такое "исе"?


Internal compiler error же.
Re[4]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 14.03.15 18:12
Оценка:
К>>Это весь код целиком.
К>>Чтобы накренилось, нужно оставить любую строчку using iceN

К>>Платформо-зависимых типов здесь нет, поэтому выбор платформы (x86, x64) не влияет.

К>>Версию компилятора я указал.

C>Это очень странно, потому что на VS 2013 w/o updates (cl version 18.0.21005.1) и на VS 2013 Update 4 (cl version 18.00.31101) никакого ICE не наблюдается, компилятор ругается только на отсутствие main. А у Вас, насколько я понимаю, VS 2013 Update 3.


Проверил сегодня на машине с Visual Studio 2013 Update 3. И впрямь, ICE. Это первый случай в моей практике. В RTM-версии все было нормально, Update 3 выдает ICE, в Update 4 опять все хорошо. Кажется, Visual Studio Team чересчур усердно стала развивать проект. Интересно еще проверить Update 2.

Вывод тут один: переходите на Update 4. Правда, неизвестно сколько багов появилось в Update 4 по сравнению с Update 3 (про по меньшей мере один, причем довольно неприятный, я знаю).
Re: (оффтоп) MSVC 2013 - ICE
От: x-code  
Дата: 14.03.15 21:16
Оценка: :))
Здравствуйте, Кодт, Вы писали:

Что нужно сделать, чтобы понять, как этот код работает?
Re[2]: (оффтоп) MSVC 2013 - ICE
От: BulatZiganshin  
Дата: 15.03.15 09:19
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Что нужно сделать, чтобы понять, как этот код работает?


мне хватило прочесть соотв. разделы на https://isocpp.org/wiki/faq (до этого C++ я знал на уровне стандарта ~91-го года)
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: (оффтоп) MSVC 2013 - ICE
От: Кодт Россия  
Дата: 15.03.15 15:28
Оценка: 18 (2)
Здравствуйте, x-code, Вы писали:

XC>Что нужно сделать, чтобы понять, как этот код работает?


Учиться, учиться и учиться

Этот код работает так.
1. template typedef вводит некую сущность
template<class T> using foo = T*;
template<class T> using bar = T*;
template<class T> using buz = foo<T>;

2. Идентичность таких сущностей — в отличие от шаблонов классов — штука тонкая. (У тех всё просто: равенство квалифицированных имён).
Надо лезть в стандарт смотреть, почему и как, но вот gcc 4.9 считает, что foo == buz, но foo != bar.
3. Идентичность/различность можно наблюдать, параметризуя этими шаблонами другие шаблоны
template<template<class>class P> void go() { std::cout << __PRETTY_FUNCTION__ << std::endl; }

int main() {
    // your code goes here
    void (*gofoo)() = go<foo>;
    void (*gobar)() = go<bar>;
    void (*gobuz)() = go<buz>;
    std::cout << (gofoo == gobar) << (gofoo == gobuz) << std::endl;
    gofoo();
    gobar();
    gobuz();
    return 0;
}

4. Компилятор реализует идентификацию через уникальные имена линкуемых символов — которые являют собой некий странный хэш от объявления. Это называется декорированными (манглированными) именами. go<foo> получает одно имя, go<bar> другое, go<buz> внезапно то же имя.

5. Длина этого декорированного имени может быть сколь угодно большой — ведь мы можем нагромоздить в параметры шаблона сколь угодно сложную конструкцию.

6. MSVC 2013sp3 в этом месте облажался, причём видно, что они просто срезали угол — все шаблоны одинаковой арности (а, вероятно, и вообще все шаблоны) манглируются строкой "??". Возможно, что у разработчиков стояло //TODO //FIXME реализовать в ближайшей версии, т.е. не успевали к релизу.
А поскольку формат манглированных имён — это мажорное изменение (влекущее за собой несовместимость библиотек), то //TODO, прощёлканное к первому релизу 2013, автоматически переехало на релиз 2015.
Перекуём баги на фичи!
Re[2]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 15.03.15 18:12
Оценка:
Здравствуйте, x-code, Вы писали:

XC>Что нужно сделать, чтобы понять, как этот код работает?


Можно начать с прочтения книги "Шаблоны C++: справочник разработчика" за авторством Вандервурда с Джосаттисом (точнее, Йосуттисом ). Правда, тут имеются некоторые вещи из C++11 (type aliases, alias templates, variadic templates), который данная книга не захватывает по причине почтенного возраста.
Re[3]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 15.03.15 18:15
Оценка:
Здравствуйте, BulatZiganshin, Вы писали:

XC>>Что нужно сделать, чтобы понять, как этот код работает?


BZ>мне хватило прочесть соотв. разделы на https://isocpp.org/wiki/faq (до этого C++ я знал на уровне стандарта ~91-го года)


Внезапно, C++ был стандартизован только в 1998 г. (ISO/IEC 14882:1998).
Re[3]: VS bug: new int(1,2,"wtf")
От: Constructor  
Дата: 15.03.15 18:50
Оценка:
Здравствуйте, Кодт, Вы писали:

К>2. Идентичность таких сущностей — в отличие от шаблонов классов — штука тонкая. (У тех всё просто: равенство квалифицированных имён).

К>Надо лезть в стандарт смотреть, почему и как, но вот gcc 4.9 считает, что foo == buz, но foo != bar.

Это баг в g++, вероятно. clang 3.5.0 и vc++ 19 (VS2015) считают по-другому, выдавая два нуля (а вот vc++ 18 из VS2013 выдает две единицы). Вот, что написано в стандарте по этому поводу (C++11, 14.5.7/2):

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced. — end note ]

Т.е. alias templates действуют аналогично шаблонам классов. Интересно, что на выходе вот такой программки святая троица (clang, g++ и vc++) выдает одни только false:

#include <iostream>
#include <type_traits>

template <class T> using foo = T*;

template <class T> using bar = T*;

template <class T> using buz = foo<T>;

template <typename T>
struct Template{};

template <typename T>
using Alias = Template<T>;

template
    <
        template <typename>
        class T1,
        template <typename>
        class T2
    >
struct is_same_template : std::false_type{};

template
    <
        template <typename>
        class T
    >
struct is_same_template<T, T> : std::true_type{};

#define CHECK_IS_SAME(T1, T2) \
    std::cout << #T1 " == " #T2 ": " << is_same_template<T1, T2>::value << std::endl

int main()
{
    std::cout << std::boolalpha;
    CHECK_IS_SAME(foo, bar);
    CHECK_IS_SAME(foo, buz);
    CHECK_IS_SAME(bar, buz);
}


К>6. MSVC 2013sp3 в этом месте облажался, причём видно, что они просто срезали угол — все шаблоны одинаковой арности (а, вероятно, и вообще все шаблоны) манглируются строкой "??". Возможно, что у разработчиков стояло //TODO //FIXME реализовать в ближайшей версии, т.е. не успевали к релизу.


Service packs для MSVS 2013 пока не выходили, обходятся update'ами.

К>А поскольку формат манглированных имён — это мажорное изменение (влекущее за собой несовместимость библиотек), то //TODO, прощёлканное к первому релизу 2013, автоматически переехало на релиз 2015.


Но ведь в MSVS 2013 Update 4 все работает (как и работало в RTM версии)!
Re[4]: VS bug: new int(1,2,"wtf")
От: Кодт Россия  
Дата: 16.03.15 10:14
Оценка:
Здравствуйте, Constructor, Вы писали:

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


К>>2. Идентичность таких сущностей — в отличие от шаблонов классов — штука тонкая. (У тех всё просто: равенство квалифицированных имён).

К>>Надо лезть в стандарт смотреть, почему и как, но вот gcc 4.9 считает, что foo == buz, но foo != bar.

C>Это баг в g++, вероятно. clang 3.5.0 и vc++ 19 (VS2015) считают по-другому, выдавая два нуля (а вот vc++ 18 из VS2013 выдает две единицы). Вот, что написано в стандарте по этому поводу (C++11, 14.5.7/2):

C>

C>When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced. — end note ]

C>Т.е. alias templates действуют аналогично шаблонам классов.

По-моему, в стандарте есть дефект. Сейчас перечитал его, но так и не понял, как из этого следует определять идентичность alias template.
Там сказано, что компилятор не будет решать уравнения
template<class X, class Y> struct original {};
template<class X> using xx = original<X,X> {};
template<class X, class Y, class Z> using xxx = original<X,X> {};
template<class X, class Y, class Z> using yyy = original<Y,Y> {};

template<template<class>class TT> void foo(TT<int>);
template<template<class,class,class>class TT> void foo(TT<int,int,int>);

original<int,int> oii;
foo(oii); // ну допустим, мы нашли в поле зрения единственное преобразование из TT<int> в original<int,int> - это xx
bar(oii); // а вот здесь уже два - xxx и yyy

в дополнение к известным ограничениям — решение уравнений с зависимыми именами и циклические ссылки зависимых имён друг на друга.


C>Но ведь в MSVS 2013 Update 4 все работает (как и работало в RTM версии)!


Очень интересно!
Перекуём баги на фичи!
Re: [оффтоп] А что делать, если оптимизатор косячит?
От: landerhigh Пират  
Дата: 16.03.15 14:49
Оценка:
Здравствуйте, Кодт, Вы писали:

Хорошо тебе, ICE.

Я вот недавно на грабли в 2012 студии наступил. После линковки в релиз попадал заведомо неправильный код. При включенном compile-time optimization. Воспроизвести, минимизировав проект, не удалось, похоже, как-то связано то ли с размером проекта, то ли еще с какой-то чертовщиной. Проблему-то удалось пофиксить малой кровью, volatile в нужном месте творит чудеса, но до сих пор не знаю, что делать. Воскурил официальный ман о том, как репортить баги оптимизатора, но там было английским по белому написано — если у вас бага проявлятся из-за оптимизации при компоновке, вы screwed.
www.blinnov.com
Re[4]: VS bug: new int(1,2,"wtf")
От: BulatZiganshin  
Дата: 16.03.15 17:40
Оценка:
Здравствуйте, Constructor, Вы писали:

C>Внезапно, C++ был стандартизован только в 1998 г. (ISO/IEC 14882:1998).


In 1985, the first edition of The C++ Programming Language was released, which became the definitive reference for the language, as there was not yet an official standard
Люди, я люблю вас! Будьте бдительны!!!
Re: MSVC 2015 - ICE
От: Кодт Россия  
Дата: 23.03.15 11:29
Оценка:
К>При том, что в VS2015 баг исправлен, — но вдруг кто-то пользуется VS2013 и страдает от айса в каких-то схожих местах.

Я нашёл способ покрешить и VS2015!

  ещё не дистиллировал
#include <iostream>

using namespace std;

// макрос для вывода "что я за функция?"
#define WHOAMI() (cout << __FUNCSIG__ << endl)

// шаблонные параметры шаблона - 1-3-арные шаблоны
#define UNARY template<class>class
#define BINARY template<class,class>class
#define TERNARY template<class,class,class>class

// генератор классов для нашей иерархии
template<class V> struct define
{
    void f(V) { WHOAMI(); }
    void g(V) { WHOAMI(); }
    void h(V) { WHOAMI(); }
    void p()  { WHOAMI(); }
};

// создадим базовые классы
// (можно было using A = define<int>, но это сделало бы отладочный вывод ещё менее читаемым)
struct A : define<int> {};
struct B : define<char> {};
struct C : define<float> {};
struct D : define<double> {};

// генератор миксина, объявляющего using членов
#define DEFINE_USING(mixin, member) \
    template<class Base, class X, class Y> struct mixin : Base { using X::member; using Y::member; }; \
    template<class Base, class X> struct mixin<Base,X,X> : Base { using X::member; }; \
    //endmacro

// перечислим интересующие нас члены
DEFINE_USING(using_f, f)
DEFINE_USING(using_g, g)
DEFINE_USING(using_h, h)
DEFINE_USING(using_p, p)
DEFINE_USING(using_q, q) // добавим один несуществующий член - это же просто имя!

// генератор плоской иерархии
template<class... Bases> struct inherit : Bases... {};

// свёртка миксином набора баз (пример левой свёртки)
template<TERNARY mixin> struct apply_mixin_l_impl
{
    template<class Base, class... Xs> struct run;

    template<class Base> struct run<Base>
    {
        using result = Base;
    };
    template<class Base, class X> struct run<Base, X>
    {
        using result = mixin<Base,X,X>;
    };
    template<class Base, class X, class Y> struct run<Base, X,Y>
    {
        using result = mixin<Base,X,Y>;
    };
    template<class Base, class X, class Y, class... Zs> struct run<Base, X,Y,Zs...>
    {
        // левая свёртка
        using tmp = mixin<Base,X,Y>;
        using result = typename run<tmp,tmp,Zs...>::result;
    };
};
template<class Base, TERNARY mixin, class... Xs> using apply_mixin_l = typename apply_mixin_l_impl<mixin>::template run<Base,Xs...>::result;

// (пример правой свёртки)
template<class Base, TERNARY mixin> struct apply_mixin_r_impl
{
    template<class... Xs> struct run // определение для случая <>
    {
        using result = Base;
    };
    template<class X> struct run<X>
    {
        using result = mixin<Base,X,X>;
    };
    template<class X, class Y> struct run<X,Y>
    {
        using result = mixin<Base,X,Y>;
    };
    template<class X, class... Ys> struct run<X,Ys...>
    {
        using folded = typename run<Ys...>::result;
        using result = mixin<folded,X,folded>;
    };
};
template<class Base, TERNARY mixin, class... Xs> using apply_mixin_r = typename apply_mixin_r_impl<Base,mixin>::template run<Xs...>::result;

// определимся, что будем использовать левую свёртку
template<class Base, TERNARY mixin, class... Xs> using apply_mixin = apply_mixin_l<Base,mixin,Xs...>;

// пример выборочного доступа к функциям
using E0 = inherit<A,B,C,D>;
using E1 = apply_mixin<E0, using_q>; // у нас нет баз с членом q, но это не должно нам помешать!
//using Efail = apply_mixin<E0, using_q, A>; // попытка сделать using A::q вызовет ошибку компиляции
using Ep = apply_mixin<E1, using_p, D>; // если заявим несколько баз, получим неоднозначность
using Ef = apply_mixin<Ep, using_f, A,B,C,D>;
using Efg = apply_mixin<Ef, using_g, A,C>; // выбор между g(int) и g(float)
using Efgh = apply_mixin<Efg, using_h, B,D>; // выбор между h(char) и h(double)

// композиция миксинов
template<class Base, class X, class Y> struct compose_mixins_impl
{
    template<TERNARY... Ms> struct run;

    //template<> struct run<> // так нельзя, С++11 и 14 запрещают явную специализацию
    template<TERNARY... Ms> struct run  // поэтому для случая <> сделаем реализацию в основном шаблоне
    {
        using result = Base;
    };
    template<TERNARY M> struct run<M>
    {
        using result = M<Base,X,Y>;
    };
    template<TERNARY M, TERNARY... Ms> struct run<M,Ms...>
    {
        using folded = typename run<Ms...>::result;
        using result = M<folded,X,Y>;
    };
};
template<TERNARY... Ms> struct compose_mixins
{
    template<class Base, class X, class Y> using mixin = typename compose_mixins_impl<Base,X,Y>::template run<Ms...>::result;
};

// чтобы дать имя шаблону, приходится указать его параметры и передать их в "вычисленный" шаблон
template<class Base, class X, class Y> using using_fgh = typename compose_mixins<using_f, using_g, using_h>::template mixin<Base,X,Y>;

// наследник, в котором все члены вытащены для всех баз
using Eall = apply_mixin<Ep, using_fgh, A,B,C,D>;

// наконец, давайте сконструируем всё вместе.
// для этого нам понадобится перечислить сразу два variadic-набора
template<class...> struct list_bases {};
template<TERNARY...> struct list_mixins {};

template<class ListBases, class ListMixins> struct install_impl;
template<class... Bs, TERNARY... Ms> struct install_impl<list_bases<Bs...>, list_mixins<Ms...>>
{
    using base = inherit<Bs...>;
    template<class B, class X, class Y> using mixin = typename compose_mixins<Ms...>::template mixin<B,X,Y>;
    using result = apply_mixin<base, mixin, Bs...>;
};
template<class ListBases, class ListMixins> using install = typename install_impl<ListBases,ListMixins>::result;

using Einst = install< list_bases<A,B,C,D>, list_mixins<using_f,using_g,using_h> >;
using Etotal = apply_mixin<Einst, using_p, D>;

// далее мы можем наследоваться от сконструированных наследников
struct Ffgh : Efgh {};
struct Fall : Eall {};
struct Ftotal : Etotal {};

// тестовая функция
template<class E>
void run()
{
    WHOAMI();
    cout << endl;
    E e;
    e.f(1);
    e.g('1');
    e.h(1.f);
    e.p();
    cout << endl << endl;
}

int main()
{
    run<Efgh>();
    run<Eall>();
    run<Etotal>();

    run<Ffgh>();
    run<Fall>();
    run<Ftotal>();
}


Кстати, можете поужасаться метапрограммированию на новомодных template alias.
Перекуём баги на фичи!
Re[2]: MSVC 2015 - ICE
От: uzhas Ниоткуда  
Дата: 23.03.15 11:41
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Я нашёл способ покрешить и VS2015!


К>[cut=ещё не дистиллировал]


/me crashed
Re[3]: MSVC 2015 - ICE
От: Кодт Россия  
Дата: 23.03.15 11:58
Оценка:
Здравствуйте, uzhas, Вы писали:

U>/me crashed


В смысле, офигел от увиденного, или компилятор айснулся?

Вообще, у меня есть воркераунд, — код, который не крешится даже на 2013.
Оба варианта писал в приступах вдохновения, независимо друг от друга, поэтому не так просто взять и ткнуть пальцем, что же там пошло не так.
Но вечером сяду за сравнение и, возможно, найду дистиллят.
Перекуём баги на фичи!
Re[4]: MSVC 2015 - ICE
От: uzhas Ниоткуда  
Дата: 23.03.15 15:01
Оценка:
Здравствуйте, Кодт, Вы писали:

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


U>>/me crashed


К>В смысле, офигел от увиденного


угу, глаза сломал при парсинге (только плиз не надо разжевывать что там происходит )
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.