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

WIP: Incorporate variance into typeck #12828

Closed
wants to merge 1 commit into from

Conversation

edwardw
Copy link
Contributor

@edwardw edwardw commented Mar 11, 2014

Currently, rustc only does so for regions substitution. Applies the same
rule to type and self parameter substitution.

With all three substitutions on, the following becomes legitimate:

fn foo<'a, 'b>(x: &'b Option<&'a int>) -> Option<&'b int> {
    *x
}

x here is contravariant with respect to the region parameter b, so
it is eligible to the return position, also contravariant.

It is also found that rustc handled one special case incorrectly. In
variance inference pass, constraints are added and then solved. But rustc
didn't add any constraint for nullary generic enum and struct so they
were classified as bivariant, the default. Then the following two
implementations of Foo:

pub struct S<T>;

impl Foo for S<int> {
    fn bar(&self) {}
}

impl Foo for S<uint> {
    fn bar(&self) {}
}

would be identical therefor conflicting under type substitution, which
is incorrect.

Closes #3598.
Closes #5781.

@alexcrichton
Copy link
Member

The travis errors look a little suspicious, but they may be spurious, just making sure that you managed to get past stage1 locally.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 11, 2014

It did. All the way to the run doc-crate-std and it failed there.

@alexcrichton
Copy link
Member

Cool, thanks!

@nikomatsakis
Copy link
Contributor

r? me -- @edwardw sorry for being incommunicado, I've been heads down on unrelated things and was planning to come back to your questions this week, but I see you've made great progress on your own.

@nikomatsakis
Copy link
Contributor

I'm not sure about the changes to inference with the nullary type. I agree it's surprising, but completely consistent, that struct Foo<T> is a subtype regardless of T. That is, the current change feels a bit ad-hoc -- I feel like we should treat unused type or lifetime parameters the same regardless of whether a struct is nullary or not. The current behavior of bivariance is theoretically the most general but not the most practical. Perhaps for cases where the parameter is unreferenced we should simply default to what people likely: covariant for types and contravariant for lifetimes (i.e., as if you had a data member with the type T or a reference with the lifetime 'a). We might even be able to remove the marker types then, if we made Unsafe<T> hardcoded to be invariant with respect to T.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 17, 2014

The current patch makes no effort to discriminate nullary type from others; it simply add covariant constraint to type parameters for all types, used or unused. As for lifetime parameters, I'll try to add constraints to them, too.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 17, 2014

By adding contravariant constraint to all lifetime parameters, we make rust more rigorous. For example, json.rs#L318 is now rejected:

pub fn buffer_encode<T:Encodable<Encoder<'a>>>(to_encode_object: &T) -> ~[u8] {
   //Serialize the object in a string using a writer
    let mut m = MemWriter::new();
    {
        let mut encoder = Encoder::new(&mut m as &mut io::Writer); // `m` does not live long enough
        to_encode_object.encode(&mut encoder);
    }
    m.unwrap()
}

@nikomatsakis
Copy link
Contributor

@edwardw Sorry, I'm confused. Why would we add a covariant constraint to all type parameters? And why would we add contravariant to all lifetimes? That seems clearly wrong. For example, a type like:

struct Foo<T> {
    x: fn(T)
}

Should be contravariant in T.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 18, 2014

As discussed in IRC, this is just another bug. The lifetime parameter and mutability in a trait reference are never used in variance inference. So the patch ends up always adding constraints for them which leads to noticeable behavior change.

@edwardw edwardw changed the title Apply variance rule to type and self parameter substitution WIP: Apply variance rule to type and self parameter substitution Mar 18, 2014
@edwardw edwardw changed the title WIP: Apply variance rule to type and self parameter substitution WIP: Incorporate variance into typeck Mar 18, 2014
@alexcrichton
Copy link
Member

I was driving by this (just taking a look), and noticed that transmutes are popping up in what I thought was completely safe code. Is the current code unsound?

@edwardw
Copy link
Contributor Author

edwardw commented Mar 19, 2014

I assume you asked about the last commit about librustdoc. Strangely enough, without the additional transmute, there is LLVM assertion failure.

@alexcrichton
Copy link
Member

Not just that, but also the "Fix fallout for stage1" commit. Fixing an LLVM assertion failure with a transmute sounds... dubious

@edwardw
Copy link
Contributor Author

edwardw commented Mar 19, 2014

Oh, that's because a bug in the variance inference that didn't take lifetime parameter and mutability of a trait reference into consideration, which is now fixed. If taking a close look, Encoder::new(&mut m as &mut io::Writer) requires a lifetime 'a, but m is just a local variable.

Yes, this is a behavior change and is open for discussion.

@alexcrichton
Copy link
Member

I'm not entirely certain how much this relates to rust-lang/rfcs#12, but if this has semantic changes I think it should go through an RFC before merging.

I still don't understand why transmutes are popping up as fallout of this change, nor why the tests had to change. I'm not entirely certain what's going on here though, so if these are soundness changes than that's fine, but unfortunate.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 19, 2014

The commit that fixes ty_trait variance inference bug causes most of the noticeable behavior change. With that bug, the variance inference pass actually treats:

pub struct Encoder<'a> {
    priv wr: &'a mut io::Writer,
}

as if it were

pub struct Encoder<'a> {
    priv wr: & io::Writer,
}

In my opinion this is a soundness issue. I couldn't find a way to fix json.rs without using transmute though.

As for the variance RFC, it asks to add constraints for unused type parameters only. The ty_trait inference bug prevents us from distinguishing unused from used reliably. The commit #2 also implements actually adding variance constraints as the RFC asks. So yes, this PR is related to it.

The transmute introduced in librustdoc is a different story. After talking to @eddyb and @huon on IRC, it seems that somehow extern "C" fn gets expanded to extern "C" extern fn after this patch. It should be able to be addressed with a proper fix without transmute.

@nikomatsakis
Copy link
Contributor

On Wed, Mar 19, 2014 at 01:20:13PM -0700, Edward Wang wrote:

Im my opinion this is a soundness issue.

Yes.

I couldn't find a way to fix json.rs without using transmute though.

Probably we will have to refactor json.rs. This happens sometimes.

Alex is definitely right though -- there should be no need for transmutes and so forth.

@edwardw
Copy link
Contributor Author

edwardw commented Mar 22, 2014

I'd like to close this PR. It has changed scope several times. The ty_trait bug fixing part is blocked by a possible json.rs refactoring if not using transmute, while the incorporating variance into typeck part is tied to adding additional variance constraints.

@edwardw edwardw closed this Mar 22, 2014
@edwardw edwardw deleted the moved-or-not branch March 23, 2014 17:16
pcwalton added a commit to pcwalton/rust that referenced this pull request Jun 28, 2014
This can break code that looked like:

    impl Foo for Box<Any> {
        fn f(&self) { ... }
    }

    let x: Box<Any + Send> = ...;
    x.f();

Change such code to:

    impl Foo for Box<Any> {
        fn f(&self) { ... }
    }

    let x: Box<Any> = ...;
    x.f();

That is, upcast before calling methods.

This is a conservative solution to rust-lang#5781. A more proper treatment (see
the xfail'd `trait-contravariant-self.rs`) would take variance into
account. This change fixes the soundness hole.

Some library changes had to be made to make this work. In particular,
`Box<Any>` is no longer showable, and only `Box<Any+Send>` is showable.
Eventually, this restriction can be lifted; for now, it does not prove
too onerous, because `Any` is only used for propagating the result of
task failure.

This patch also adds a test for the variance inference work in rust-lang#12828,
which accidentally landed as part of DST.

Closes rust-lang#5781.

[breaking-change]
bors added a commit that referenced this pull request Jun 28, 2014
I believe that #5781 got fixed by the DST work. It duplicated the
variance inference work in #12828. Therefore, all that is left in #5781
is adding a test.

Closes #5781.

r? @huonw
pcwalton added a commit to pcwalton/rust that referenced this pull request Jul 1, 2014
… borrows

still in scope".

This issue was fixed by PR rust-lang#12828 and rust-lang#5781. All that was left was to
add tests.

Closes rust-lang#12223.
bors added a commit that referenced this pull request Jul 2, 2014
still in scope".

This issue was fixed by PR #12828 and #5781. All that was left was to
add tests.

Closes #12223.
fasterthanlime pushed a commit to fasterthanlime/rust that referenced this pull request Jul 22, 2022
… r=Veykril

Rename proc macro server from 'Rustc' to 'RustAnalyzer'

Related to:

  * rust-lang/rust-analyzer#12818

This is mostly a courtesy PR for the sake of rustc maintainers. When they looked at `proc-macro-srv`, they noticed the server was named `Rustc` — probably because of historical copy-paste. Only rustc's proc macro server should be named `Rustc`, ra's can be named `RustAnalyzer`.

Maintainer impact: There's no semantic changes in this PR, only naming. One test snapshot was updated since "proc macro server types" were used to test traits somewhere else and I renamed those too, why not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants