(resending a mail I wrote earlier, since I forgot to reply to all)
Hi Patrick,
Thanks for bringing this up. This is definitely something we should
fix. I had in mind similar changes to what you suggested, but I don't
think it's necessary to limit what paths can appear in `use`
statements (though it might be ok as an interim step).
This is tied into a change I had in mind with respect to the way that
we handle substitutions. Right now we concatenate all type parameters
that are in scope into one big list. I think it would be better to
have multiple "namespaces" for type parameters. Currently (and
probably in perpetuity) there would be two: impl/trait + fn. To put it
more concretely, instead of representing the subsitutions as
`~[ty::t]`, it would be `[~[ty::t], ..2]`. Therefore, we wouldn't
"concatenate" type parameters anymore but rather just combine them.
This relates to resolve because the output of resolve should be a def
paired with a (possibly empty) set of type parameter values for each
namespace. The basic algorithm stays unchanged, except that it carries
along extra information (the type parameters values).
Speaking of resolve, the current algorithm is still a fixed point
iteration, correct? Were there plans to revise the algorithm? I can't
recall anymore.
Niko
On Sun, Apr 28, 2013 at 10:38:35AM -0700, Patrick Walton wrote:
> Hi everyone,
>
> The reactions to this bug on impls [1] have caused me to think that
> the current treatment of paths in generic type and trait
> implementations is something of a wart and perhaps should be
> reworked. Specifically, the problem is that this:
>
> impl<T> MyType<T> {
> fn new<U>() -> MyType<T> { ... }
> }
>
> Cannot be accessed (as you might expect) like so:
>
> MyType::<int>::new::<float>()
>
> But instead you must concatenate the type parameters like this:
>
> MyType::new::<int,float>()
>
> This is highly unintuitive.
>
> Basically, all of this is an artifact of the fact that, internally,
> we treat a type implementation as essentially a module, and modules
> don't have type parameters. This fact also brings with it an
> unfortunate issue relating to typedefs, namely that you cannot call
> static methods through them. You might wish to write:
>
> type IntMyType = MyType::<int>;
>
> MyIntType::new::<float>()
>
> But you can't. In fact, you can't call static methods *at all*
> through typedefs, meaning that this doesn't work:
>
> impl MyOtherType {
> fn new() -> MyOtherType { ... }
> }
>
> type Alias = MyOtherType;
>
> Alias::new() // doesn't work
>
> This severely reduces the utility of typedefs.
>
> I've been thinking about ways we could fix this without severely
> complicating the compiler, and I think I've got the sketch of an
> idea that might work. I propose that we change type implementations
> (and traits) to be less module-like in the following ways:
>
> 1. Forbid `use` statements from importing from type implementations
> or traits. Having `use` statements take type parameters would
> severely complicate an already complex resolution pass, and might
> even require the name resolution and typechecking phases to be
> intertwined in an incoherent way.
>
> 2. Add a new type of `path` to the grammar:
>
> '::'? identifier ('::' identifier)* '::' '<' type_parameters '>'
> '::' identifier ('::' identifier)* ('::' '<' type_parameters '>')?
>
> This production admits paths like `MyType::<int>::new::<float>()`.
>
> 3. Internally, whenever the resolve pass sees one of these paths, or
> sees a component in a pass resolve to a typedef, it drops the type
> parameters and follows any typedefs to find the associated `impl` or
> trait.
>
> 4. When the typechecker sees such a path, it resolves the type
> portion of that path. If the type didn't resolve to a nominal
> monotype, it reports an error. Otherwise, it pulls the type
> parameters out of that nominal type and concatenates them with the
> type parameters supplied in the rest of the path, if any, to produce
> the "real" set of type parameters used by typechecking and
> translation.
>
> These semantics should allow all type parameters to be dropped if
> they can be inferred, as today, but should allow typedefs to "just
> work". Unless I'm missing something. :)
>
> This is not a backwards compatible change, but I don't expect much
> code to be impacted: I suspect `use` from a trait or type
> implementation is rare, and concatenated type parameters even more
> so.
>
> Thoughts?
>
> Patrick
>
> [1]: https://github.com/mozilla/rust/pull/6087
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev