Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug in generic trait implementation? #3744

Closed
tav opened this issue Oct 12, 2012 · 9 comments
Closed

Bug in generic trait implementation? #3744

tav opened this issue Oct 12, 2012 · 9 comments
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically A-trait-system Area: Trait system
Milestone

Comments

@tav
Copy link
Contributor

tav commented Oct 12, 2012

There is a possible bug when you define a generic trait implementation within a different module but within the same file, e.g. given:

type byte = u8;

mod crypto {

// Hash is the trait implemented by all cryptographic hash functions.
pub trait Hash {
    fn block_size() -> int;
    fn digest() -> ~[byte];
    fn digest_size() -> int;
    fn reset();
    fn update(msg: &[byte]);
}

// HashUtil is a utility trait that is auto-implemented for all Hash
// implementations.
pub trait HashUtil {
    fn hexdigest() -> ~str;
    fn update(msg: &str);
}

impl <A: Hash> A: HashUtil {

    fn hexdigest() -> ~str {
        let mut d = ~"";
        for vec::each(self.digest()) |b| {
            d += fmt!("%02x", *b as uint)
        }
        return d;
    }

    fn update(msg: &str) {
        self.update(str::to_bytes(msg));
    }

}

}

mod sha256 {

use crypto::Hash;

const block_size: int = 64;
const digest_size: int = 32;

const i0: u32 = 0x6a09e667;
const i1: u32 = 0xbb67ae85;
const i2: u32 = 0x3c6ef372;
const i3: u32 = 0xa54ff53a;
const i4: u32 = 0x510e527f;
const i5: u32 = 0x9b05688c;
const i6: u32 = 0x1f83d9ab;
const i7: u32 = 0x5be0cd19;

const k: [u32 * 64] =
    [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
     0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
     0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
     0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
     0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
     0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
     0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
     0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
     0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];

struct sha256 {
    b: [mut byte * 64],
    h: [mut u32 * 8],
    mut l: u64,
    mut n: uint,
}

impl sha256 {

    #[inline(always)]
    fn compute_block() {
        let mut h0 = self.h[0];
        let mut h1 = self.h[1];
        let mut h2 = self.h[2];
        let mut h3 = self.h[3];
        let mut h4 = self.h[4];
        let mut h5 = self.h[5];
        let mut h6 = self.h[6];
        let mut h7 = self.h[7];
        self.h[0] = h0;
        self.h[1] = h1;
        self.h[2] = h2;
        self.h[3] = h3;
        self.h[4] = h4;
        self.h[5] = h5;
        self.h[6] = h6;
        self.h[7] = h7;
    }

}

impl sha256: Hash {

    fn block_size() -> int { block_size }

    fn digest() -> ~[byte] {
        ~[1, 2, 37]
    }

    fn digest_size() -> int { digest_size }

    fn reset() {
        self.h[0] = i0;
        self.h[1] = i1;
        self.h[2] = i2;
        self.h[3] = i3;
        self.h[4] = i4;
        self.h[5] = i5;
        self.h[6] = i6;
        self.h[7] = i7;
        self.l = 0;
        self.n = 0;
    }

    fn update(msg: &[byte]) {
        let mut l = msg.len();
        self.l += l as u64;
        if self.n > 0 {
            if l > 64 - self.n {
                l = 64 - self.n;
            }
        }
    }

}

pub fn new() -> sha256 {
    sha256{
        b: [mut 0, ..64],
        h: [mut i0, i1, i2, i3, i4, i5, i6, i7],
        l: 0,
        n: 0
    }
}

}

fn main() {
    io::println(fmt!("val: %?", sha256::new().block_size()));
    io::println(fmt!("val: %?", sha256::new().hexdigest()));
}

The compiler errors with:

error: attempted access of field `hexdigest` on type `sha256::sha256`, but no field or method with that name was found

If, however, you put a use crypto::*; statement before the main(), then everything seems to work. This behaviour feels like a bug since block_size() works and hexdigest() is defined within the same module...

@catamorphism
Copy link
Contributor

Not sure what the bug is here? block_size works because you imported its containing trait, Hash, explicitly. hexdigest doesn't because you didn't import its containing trait, HashUtil. You could remove use crypto::*; and say use crypto::HashUtil to also get a program that compiles.

But maybe I'm misunderstanding?

@tav
Copy link
Contributor Author

tav commented Oct 12, 2012

The Hash trait is only imported in the sha256 module. Yet it works without a use crypto::Hash; before the main().

In contrast, it requires an explicit use crypto::HashUtil; before the main(). Surely this isn't the intended behaviour?

@catamorphism
Copy link
Contributor

Sorry, I didn't read your code correctly at first. Are you saying that if you comment out the line in main with the call to hexdigest, it works without inserting a use crypto::*;?

@tav
Copy link
Contributor Author

tav commented Oct 12, 2012

Yes :)

@catamorphism
Copy link
Contributor

Okay, weird; I'll try to reproduce when I get a moment.

@ghost ghost assigned catamorphism Oct 12, 2012
@catamorphism
Copy link
Contributor

This actually works as intended; you just need to use crypto::HashUtil in the scope where main is declared. But why don't you need to use crypto::Hash explicitly? Because there's an impl of Hash for sha256. Since the impl of HashUtil is generic, you have to use it explicitly.

It still seems a little weird to me, though. Maybe @pcwalton could comment?

@catamorphism
Copy link
Contributor

I'm just going to close this since I think it's intended behavior. If you think it should change, feel free to file an RFC bug :-)

@nikomatsakis
Copy link
Contributor

I actually didn't think that was the intended behavior. I thought you had to import traits to invoke trait methods, and I didn't think it was relevant where the impl for the trait appeared. @pcwalton is this incorrect?

@nikomatsakis nikomatsakis reopened this Oct 16, 2012
@nikomatsakis
Copy link
Contributor

Seems like this is expected. Closing.

RalfJung pushed a commit to RalfJung/rust that referenced this issue Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-resolve Area: Name/path resolution done by `rustc_resolve` specifically A-trait-system Area: Trait system
Projects
None yet
Development

No branches or pull requests

3 participants