gccrs icon indicating copy to clipboard operation
gccrs copied to clipboard

Type error in to_le implementation

Open bjorn3 opened this issue 2 years ago • 1 comments

I tried this code:

fn to_le(this: u32) -> u32 {
    #[cfg(target_endian = "little")]
    {
        this
    }
    #[cfg(not(target_endian = "little"))]
    {
        this.swap_bytes()
    }
}

I expected to see this happen: Compiles

Instead, this happened:

<source>:3:5: error: expected [()] got [u32]
    3 |     {
      |     ^
<source>:1:1: error: expected [u32] got [()]
    1 | fn to_le(this: u32) -> u32 {
      | ^                      ~
Compiler returned: 1

Meta

  • What version of Rust GCC were you using, git sha if possible.
gccrs (Compiler-Explorer-Build-gcc-8809ee8c6a5621e830f3cfe66c381f986e63c7f2-binutils-2.38) 12.0.1 20220118 (experimental)

bjorn3 avatar Aug 08 '22 10:08 bjorn3

The reason for this is actually a bit weird. rustc stores the content of blocks as a list of statements, while we keep a list of statements and a tail expression. When expanding and stripping our tail expression (like here on little-endian systems), this doesn't cause the previous statement to become the tail expression like it does on rustc, as shown by our AST pretty printer:

fn swap_bytes(this: u32) -> u32 {
	this /* tail expr */
}


fn to_le(this: u32) -> u32 {
	{
		this /* tail expr */
	}
; /* stmt */
	{
		swap_bytes(
			this,
		) /* tail expr */
	}
 /* tail expr */
}
fn swap_bytes(this: u32) -> u32 {
	this /* tail expr */
}


fn to_le(this: u32) -> u32 {
	{
		this /* tail expr */
	}
; /* stmt */

}

This explains the second error, where we expect the function to return a u32 but get the unit type as there is no tail expression.

Because of this, we also get the first typecheck error: If we were to compile the following code with rustc

fn swap_bytes(this: u32) -> u32 {
    /* dummy */
    this
}

fn to_le(this: u32) -> u32 {
    {
        this
    }
    {
        swap_bytes(this)
    }
}

we'd get a similar error, since the first block would be understood as a statement and not an expression:

error[E0308]: mismatched types
 --> test-rustc.rs:8:9
  |
8 |         this
  |         ^^^^ expected `()`, found `u32`
  |
help: you might have meant to return this value
  |
8 |         return this;
  |         ++++++     +

So the fix is in theory quite simple: Simply only keep a list of statements in our BlockExpr type. However, unlike rustc, our statements are not usable as expressions: only our expressions are usable as statements. So this is actually a really big change which is gonna be annoying to deal with.

@philberty, or anyone, if you have any thoughts before I investigate further or start fixing the issue let me know.

CohenArthur avatar Aug 11 '22 17:08 CohenArthur

Fixed by #2156

CohenArthur avatar May 11 '23 08:05 CohenArthur