(я все еще новичок в SO и не уверен, что «ну, может быть» квалифицируется как ответ, но вот, пожалуйста.)
Отказ от ответственности: правил для такого рода вещей нет (пока) высечены в камне.Итак, окончательного ответа пока нет.Я собираюсь сделать некоторые предположения, основанные на (а), какие виды преобразований компилятора LLVM делает / мы в конечном итоге захотим сделать, и (б) какие модели у меня в голове, которые будут определять ответ на этот вопрос.
Кроме того, я вижу две части этого: перспектива компоновки данных и перспектива наложения имен.Проблема с компоновкой заключается в том, что NotReallyImmutable
может в принципе иметь совершенно иную компоновку, чем ReallyImmutable
.Я не знаю много о разметке данных, но с UnsafeCell
становится repr(transparent)
и это единственное различие между этими двумя типами, я думаю, намерение это для того, чтобы это работало.Однако вы полагаетесь на то, что repr(transparent)
является «структурным» в том смысле, что это должно позволить вам заменять вещи более крупными типами, что, я не уверен, было записано где-либо явно.Похоже на предложение о последующем RFC, который соответствующим образом расширяет гарантии repr(transparent)
?
Что касается псевдонимов, проблема заключается в нарушении правил вокруг &T
.Я бы сказал, что до тех пор, пока у вас нигде нет живого &T
, когда вы пишете через &UnsafeCell<T>
, вы хороши - но я не думаю, что мы можем гарантировать это еще.Давайте рассмотрим более подробно.
Перспектива компилятора
Соответствующие оптимизации - это те, которые используют &T
только для чтения.Поэтому, если вы переупорядочите последние две строки (transmute
и присвоение), этот код, скорее всего, будет UB, так как мы можем захотеть, чтобы компилятор мог «предварительно извлекать» значение за общей ссылкой и повторно использовать это значениепозже (т.е. после встраивания этого).
Но в вашем коде мы будем генерировать аннотации "только для чтения" (noalias
в LLVM) после того, как transmute
вернется, и данные действительно будут прочитанытолько начиная там.Итак, это должно быть хорошо.
Модели памяти
"Наиболее агрессивные" из моих моделей памяти по сути утверждают, что все значения всегда действительны , и я думаю, что даже этомодель должна быть в порядке с вашим кодом.&UnsafeCell
- это особый случай в этой модели, где валидность просто прекращается, и ничего не сказано о том, что стоит за этой ссылкой.В тот момент, когда transmute
возвращается, мы берем память, на которую он указывает, и делаем все это только для чтения, и даже если мы сделали это "рекурсивно" через Rc
(чего не делает моя модель, но только потому, что я не могне поймете, как это сделать), все будет хорошо, так как вы больше не будете мутировать после transmute
.(Как вы могли заметить, это то же ограничение, что и в перспективе компилятора. Смысл этих моделей, в конце концов, заключается в том, чтобы разрешить оптимизацию компилятора.;)
(Как примечание, я действительно хочуСейчас miri была в лучшей форме. Кажется, мне нужно попытаться заставить проверку работать снова там, потому что тогда я могу сказать вам, что нужно просто запустить ваш код в miri, и он скажет вам, подходит ли эта версия моей моделичто вы делаете: D)
Я думаю о других моделях, которые в настоящее время проверяют только «при доступе», но еще не разработали историю UnsafeCell
для этой модели.Этот пример показывает, что модель может содержать пути «фазового перехода» памяти: сначала UnsafeCell
, но затем с нормальным совместным использованием с гарантиями только для чтения.Спасибо за то, что подняли этот вопрос, и это послужит хорошим примером для размышлений!
Итак, я думаю, что могу сказать, что (по крайней мере, с моей стороны) есть намерение 1047 *, чтобы позволитьэтот вид кода, и это, похоже, не препятствует какой-либо оптимизации.Удастся ли нам найти модель, с которой все могут согласиться и которая все еще позволяет это, я не могу предсказать.
Противоположное направление: T -> UnsafeCell<T>
Теперь это более интересно.Проблема в том, что, как я сказал выше, вы не должны иметь &T
live при записи через UnsafeCell<T>
.Но что здесь означает «жить»?Это сложный вопрос!В некоторых моих моделях это может быть настолько слабым, что «ссылка такого типа существует где-то, а время жизни все еще активно», т. Е. Не может иметь никакого отношения к тому, действительно ли ссылка используется используется .(Это полезно, потому что это позволяет нам проводить больше оптимизаций, например, перемещать нагрузку из цикла, даже если мы не можем доказать, что цикл когда-либо выполняется - что привело бы к использованию неиспользуемой ссылки в противном случае.) И так как &T
- это Copy
, вы даже не можете избавиться от такой ссылки.Итак, если у вас есть x: &T
, то после let y: &UnsafeCell<T> = transmute(x)
старый x
все еще существует и его время жизни все еще активно, поэтому запись через y
вполне может быть UB.
Я думаю, что вы 'Я должен каким-то образом ограничить псевдонимы, которые &T
позволяет, очень тщательно следя за тем, чтобы никто не сохранил такую ссылку.Я не собираюсь говорить «это невозможно», потому что люди продолжают удивлять меня (особенно в этом сообществе;), но я не могу придумать, как сделать эту работу.Мне было бы любопытно, если у вас есть пример, хотя вы думаете, что это разумно.