2014-10-02 9 views
9

La mia comprensione è che il codice seguente ha un comportamento non definito in C++ a causa di qualcosa chiamato "regola di aliasing rigoroso".Aliasing rigoroso in Rust?

#include <cstdint> 

enum Foo : int16_t {}; 

void test(Foo& foo) { 
    reinterpret_cast<int16_t&>(foo) = 42; 
} 

In particolare, un compilatore C++ può omettere l'assegnazione complessivamente perché è permesso assumere che la int16_t& restituito dal reinterpret_cast non punta alla stessa memoria, come foo (di tipo Foo&) perché i loro tipi sono diverso.

La mia domanda è, Rust ha qualcosa di simile alla "rigida regola di aliasing" del C++? In altre parole, il seguente codice Rust equivalente ha un comportamento indefinito?

#[repr(i16)] 
enum Foo { Dummy } 

unsafe fn test(foo: &mut Foo) { 
    *std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42; 
} 

EDIT:
C'è un problema non correlato con il codice di esempio Rust sopra che test crea una variante enum non esistente. Quindi, ecco lo stesso codice con una soluzione rapida:

#[repr(i16)] 
enum Foo { Dummy, Yummy = 42 } 

unsafe fn test(foo: &mut Foo) { 
    *std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42; 
} 
+0

Per quanto ne so: C/C++ dice essenzialmente "scrivere memoria come un tipo e leggerlo come un tipo diverso non va mai bene, tranne se si sta usando un 'union', o si legge come' char' " . La ruggine non ha una tale regola generale e la legalità dipende dalla particolare coppia di tipi in questione. – glaebhoerl

risposta

6

mia comprensione è che il codice della ruggine generale (come verificato dal compilatore), naturalmente, non contiene alcun comportamento non definito (salvo errori del compilatore), e che la ruggine-the- la lingua non definisce alcun comportamento indefinito, diversamente da C.

Naturalmente, l'unico comportamento non definito potrebbe essere nei blocchi unsafe o nelle funzioni. I blocchi unsafe hanno lo scopo di incapsulare qualsiasi comportamento potenzialmente pericoloso come being safe as a whole. A post from July 2014 menziona che il compilatore di Rust richiede che determinati invarianti siano soddisfatti e che sia generalmente dangerous to break those invariants, even in unsafe blocks (in effetti, è possibile interrompere solo entro i blocchi unsafe).

Uno di questi comportamenti pericolosi è pointer aliasing (che sembra essere definito in LLVM stesso). È interessante notare, tuttavia, la documentazione llvm dicono (sottolineatura mia):

conseguenza, tipo a base di analisi alias, alias TBAA, alias -fstrict-aliasing, è non applicabile al generale disadorna LLVM IR

Pertanto, sembra che in genere, fino a quando nessuno degli altri comportamenti non sicuri viene attivato a causa del blocco unsafe, l'aliasing rigoroso non è un problema in Rust.


Detto questo, il tuo esempio specifico è forse pericoloso, perché sembra corrispondere a uno dei comportamenti non sicuri dal di riferimento:

Valori non validi in tipi primitivi, anche in campi privati ​​/ locali :

  • una discriminante in un enum non incluso nella definizione del tipo

ho scritto una piccola estensione per il tuo esempio che mostra le rappresentazioni binarie delle varianti enum, prima e dopo l'operazione non sicura: http://is.gd/x0K9kN

Come si può vedere, assegnando 42 al valore enum non corrisponde nessuna delle definito i valori discriminatori. Se, tuttavia, aveste assegnato 0 o 1 al valore enum (o aveste invece definito discriminatori espliciti per le varianti enum), allora l'operazione dovrebbe teoricamente andar bene.

+2

"Se, tuttavia, avevi assegnato 0 o 1 al valore enum (o invece avevi definito i discriminatori espliciti per le varianti enum), allora l'operazione dovrebbe teoricamente andar bene." Il layout Enum è in realtà indefinito, quindi, senza espliciti descriminanti, anche assegnare 0 o 1 è probabilmente un comportamento non definito (cioè, il compilatore può scegliere di non usare 0 e 1). – huon

+0

@dbaupp: I documenti attuali sembrano essere un po 'sparsi su questo - non sono riuscito a trovare da nessuna parte che dice esplicitamente che il layout enum di tipo C non è definito (le enumerazioni C-like sono già un caso speciale, comunque). Tuttavia, ho trovato un articolo precedente che [afferma che le discriminanti automatiche iniziano con 0] (https://gist.github.com/brson/9dec4195a88066fa42e6#enumerations), ma non sembra far parte dei documenti attuali più. – voithos