Doge
Что и является в неком роде тем самым дак тайпингом о котором тут и говорили
Ilya
Doge
Потому что в компиляторе именно для IDisposable + using + value типа зашито это поведение
Ilya
Roman
Ilya
Ilya
Ilya
мы точно об одном говорим?
Roman
нет
Ilya
нет
тогда как это относится к using и Dispose?
Ilya
вот какого хера Dispose на реф структах работает в юзинге, а в обычных нет???
Ilya
имеется в виду что имплементация IDisposable не нужна на реф структах
Ilya
почему нельзя было сделать для любых типов
Ilya
это же все компайл тайм сахар с юзингом
Ilya
но ведь это выгодно для структур
Ilya
нет каста к интерфейсам -_-
Ilya
почему только для реф структур - неясно
Ilya
прям зОгадка
Ilya
Не понял ща, чем выгодно в данном случае?
Ilya
Без каста в тырфейс
Ilya
В каком месте каст?
Ilya
В юзинге для структур нет каста
Ilya
Бля в обычных структурах. В обычных
Ilya
Именно, поэтому я не понял, чем тебе поможет дипозабл для структур
Ilya
На дак тацйпинге
Ilya
затем, чтобы struct A : IDisposable {}; using var a = new A(); работало без боксинга
Doge
На дак тацйпинге
Ну смотри пример с дак тайпингом (прям настоящим) для ref struct'ов:
https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANgHwAEAmARgFgAoKwgZgAJYAzegZwygFcwN6BBegG8q9enTEp6AEWysADhFYwAFAEohI0VsKkAnMoBEAGQi4DqgNyaAvlVvVK4kvQDCGylrENCkgLJr3T09CAAZlADsYAHd+NXVhDyCknX0DADkIXlxTcytEpPtPe2sgA===
Doge
Вот тебе настоящий дак тайпинг, даже без всякого IDisposable и каста в него
Doge
А вот и сам дак тайпинг в компиле для желающих: https://github.com/chsienki/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs#L199
Причём он работает и для асинк диспозаблов, внезапно:
https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANgHwAEAmARgFgAoQgBgAJDSBWAbiqsIGY6BnDKAK5gMdAIKi6Abyp0G3QkjoARbDwAOEHjFE8AngDswACgCUdALwA+GbNsKAdACUB+o6YuWGpAJxGARAAyELh+JiZslFQAvuzU3LAAZrz8QiIS0pSyXAwoyqoaWu4ZtnY+/kEh4TYxlDUc8sR0AMJSNlnyuQCyRW0lDDRG+jAA7mKmZsV9U4y+fgByECK4waERU7I1JZu2vdmEABwMip2kPZnrhN79gyNiouOt5+uls6IAykrvSqu9W701USAA==
Крылатый
А меж тем https://twitter.com/wesleywiser/status/1354896012113022984
Ayrat
Крылатый
Бегу!
Doge
Ilya
Ilya
вот прям хоть убей
Ilya
IL_000c: constrained. A
IL_0012: callvirt instance void [System.Private.CoreLib]System.IDisposable::Dispose()
Doge
И про такую реализацию явно сказано в lowering'е для disposable, который я выше кидал
Doge
Если ты попробуешь сделать каст в свой интерфейс (не диспозабл), то ты получишь бокс в IL
Ilya
Ilya
Ilya
https://t.me/fsharp_flood/284148
Doge
а этот кейс тут причём?
Потому что вся работа с IDisposable'ом + структурами явно зашита в компиль, чтобы не генерить бокс, который будет, если это преобразование кода делать наивно.
Это прям конкретный хак под это конкретное место, о котором напрямую в комменте и сказано.
Ilya
IL_0000: nop
IL_0001: ldarga.s item
IL_0003: constrained. !!T
IL_0009: callvirt instance void [System.Private.CoreLib]System.IDisposable::Dispose()
IL_000e: nop
IL_000f: ret
Doge
Doge
Это опять IDisposable
Doge
Ты попробуй с любым другим интерфейсом
Ilya
Ilya
какой диспозабл?
Ilya
Только метод ты генеришь для T: IDisposable
https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgIwFgBQyAMABMugHQAyYAdgI4DcO+R6ALPbgMxGoEDCBA3jgLCinZMwIBZdAB4AKgD4AFHIJhgAUwC2ASgDuACw0AnDQTkgAkgEEBQkcPXaSAETABnAA4B7dxqU6bA4AvvbCYQQRyJxUmsYAZgCGAMZmNhGC2A7iBG5evv6BEaFZIlGc7sDGAK7JwATWVtYZEdliEnk+fgH8JSFhOMFAA===
Ilya
сорян
Ilya
IL_0000: nop
IL_0001: ldarga.s item
IL_0003: constrained. !!T
IL_0009: callvirt instance void C/IA::Dispose()
IL_000e: nop
Doge
Doge
Ты попробуй явно каст сделать и увидешь результат
Ilya
Ilya
в юзинге
Doge
Ilya
то. что декомпилятор показывает тебе как каст - не каст
Doge
то. что декомпилятор показывает тебе как каст - не каст
https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgIwFgBQyAMABMugHQAyYAdgI4DcO+R6ALPbgMxGoEDCBA3jgLCinZMwIBZdAAoAlIOwiRMmQEkAgnMoBTAO4EN8uSQAiYAM4AHAPYWd8tsoC+QkW+EfRBKsB0AnADMAQwBjHQJNL0VlcQJzazsHOScRVyV3DOFkTgtgfwBXUOBDECisgS9YsQkE23t5fnSXDxxnIA===
Doge
Я про такой пример
Doge
Который и должен получаться при наивном lowering'е IDIsposable, но который вручную заменяется в компиляторе, чтобы бокса избежать
Doge
Вот тебе буквально комментарий из исходников компилятора, как оно должно быть по спеке и как они делают, чтобы не было бокса:
// SPEC: When ResourceType is a non-nullable value type, the expansion is:
// SPEC:
// SPEC: {
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { ((IDisposable)resource).Dispose(); }
// SPEC: }
// SPEC:
// SPEC: Otherwise, when Resource type is a nullable value type or
// SPEC: a reference type other than dynamic, the expansion is:
// SPEC:
// SPEC: {
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); }
// SPEC: }
// SPEC:
// SPEC: Otherwise, when ResourceType is dynamic, the expansion is:
// SPEC: {
// SPEC: dynamic resource = expr;
// SPEC: IDisposable d = (IDisposable)resource;
// SPEC: try { statement; }
// SPEC: finally { if (d != null) d.Dispose(); }
// SPEC: }
// SPEC:
// SPEC: An implementation is permitted to implement a given using statement
// SPEC: differently -- for example, for performance reasons -- as long as the
// SPEC: behavior is consistent with the above expansion.
//
// In the case of using-await statement, we'll use "IAsyncDisposable" instead of "IDisposable", "await DisposeAsync()" instead of "Dispose()"
//
// And we do in fact generate the code slightly differently than precisely how it is
// described above.
//
// First: if the type is a non-nullable value type then we do not do the
// *boxing conversion* from the resource to IDisposable. Rather, we do
// a *constrained virtual call* that elides the boxing if possible.
//
// Now, you might wonder if that is legal; isn't skipping the boxing producing
// an observable difference? Because if the value type is mutable and the Dispose
// mutates it, then skipping the boxing means that we are now mutating the original,
// not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}"
// and r is out of scope after the finally, so it is not possible to observe the mutation,
// or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}",
// so again, we are not mutating x to begin with; we're always mutating a copy. Therefore
// it doesn't matter if we skip making *a copy of the copy*.
Doge
И по спеке там везде должен быть каст
Ilya
Ilya
вот компилятор делает ровно это
Ilya
и делает он это потому что нельзя сравнивать структуры с null
Ilya
разница между не структурами - проверка на null
Ilya
Ilya
поэтому у тебя такое поведенеи
Doge
И там в IL явные:
IL_001a: box C/A
IL_001f: callvirt instance void [System.Private.CoreLib]System.IDisposable::Dispose()
IL_0024: nop
Doge
Это мы буквально получим, если будем делать lowering по спеке
Ilya
Ilya
вообще каст там нужен для имплисит реализации
Ayrat
Начал интервьюировать в МС, кадры как всегда поражают.
Ждите прохладных историй
Sergey