impl Aspect Anonymous Type Parameters
Summary
Define the syntax, desugaring rules, and restrictions for impl Aspect as an anonymous bounded type parameter in function parameter position. RFC-0002 Q3 proposed this feature; this RFC resolves the design details before implementation begins.
Supersedes: RFC-0002 (partial — this RFC specifies the impl Aspect shorthand form; note that RFC-0034 superseded RFC-0002 Q2, so inline + multi-bound is now valid for named type parameters)
Target: v0.7.0
Motivation
Named type parameters are verbose when a type variable is used only once and is never cross-referenced within the signature:
// Named — T appears only once and is never referenced again
fun print_all<T: Printable>(items: T[]) { ... }
// Proposed shorthand
fun print_all(items: impl Printable[]) { ... }
Every single-use bounded parameter currently forces an explicit type variable name that adds no information. The RFC-0002 decision to include impl Aspect was correct, but the semantic details were deferred. This RFC pins them down.
Goals
- Define the desugaring strategy for
impl Aspectin parameter position. - Define the semantics of multiple
impl Aspectoccurrences in one signature. - Define the positions where
impl Aspectis and is not permitted. - Define error message requirements for violated anonymous bounds.
- Define the interaction with explicit named type parameters in the same signature.
Non-Goals
impl Aspectin return position — deferred to RFC-0037.impl Aspectin struct fields or as a type alias — existential types (RFC-0038) and aspect aliases (RFC-0039) cover those cases respectively.
Design
impl Aspect is Pure Syntactic Sugar
impl Aspect in parameter position desugars to a fresh anonymous named type parameter during an AST lowering pass that runs before the typechecker. The typechecker never sees impl Aspect; it sees only named type parameters.
fun foo(x: impl Display)
// desugars to:
fun foo<_T0: Display>(x: _T0)
fun print_all(items: impl Printable[])
// desugars to:
fun print_all<_T0: Printable>(items: _T0[])
The lowering pass is a dedicated phase between parsing and typechecking. It walks every ImplAspect type expression node and replaces it with a fresh TypeParam whose bound is the named aspect, attaching source-spelling metadata to the generated variable (see Error Messages below).
Multiple Occurrences Are Independent
Each impl Aspect occurrence in a signature generates a fresh, independent type variable. Two parameters typed impl Comparable are not required to have the same concrete type:
fun compare(a: impl Comparable, b: impl Comparable) -> boolean { ... }
// desugars to:
fun compare<_T0: Comparable, _T1: Comparable>(a: _T0, b: _T1) -> boolean { ... }
compare(1, "hello") is valid as long as both Int and String implement Comparable. To constrain two parameters to the same type, use a named type parameter: fun compare<T: Comparable>(a: T, b: T).
Permitted Positions
impl Aspect is permitted only in function parameter position in this RFC.
| Position | Permitted? | Note |
|---|---|---|
| Function parameter type | Yes | This RFC |
| Function return type | No | RFC-0037 |
| Struct field type | No | RFC-0038 (dyn Aspect) |
| Type alias | No | RFC-0039 (aspect alias syntax) |
let/mut binding annotation | No | Use a named param |
The parser (or a post-parse validation pass) must reject impl Aspect in all non-parameter positions with an error that names the correct RFC for the deferred case.
Mixing with Named Type Parameters
impl Aspect and named type parameters may coexist freely in the same signature. The anonymous parameter has no special relationship to any named parameter:
fun zip<T>(a: impl Iterable, b: T) -> Pair<T, T> { ... }
// desugars to:
fun zip<T, _T0: Iterable>(a: _T0, b: T) -> Pair<T, T> { ... }
Error Messages
When a bound is not satisfied, error messages must reference the source spelling (impl Display), not the generated internal name (_T0). The desugaring pass annotates each generated TypeParam with:
source_spelling: String— the text"impl Display"as written by the programmersource_span: Span— the span of theimpl Displaynode in the source
The typechecker uses source_spelling when generating T0012 for an anonymous parameter:
error[T0012]: argument does not implement Display
--> main.mtl:3:12
fun foo(x: impl Display)
^^^^^^^^^^^^ this parameter requires Display
Never expose the generated name (_T0) in user-facing error messages.
Grammar
New production added to type_expr:
impl_type = { "impl" ~ type_expr }
// Added as an alternative in type_expr — parameter position only
A validation pass after parsing rejects impl_type nodes that appear outside function parameter position.
AST
New TypeExpr variant for the pre-lowering AST:
pub enum TypeExpr {
// ... existing variants ...
ImplAspect {
bound: Box<TypeExpr>,
source_span: Span,
},
}
The lowering pass eliminates all ImplAspect nodes before the typechecker runs, replacing each with a generated TypeParam and recording the source spelling for error messages.
Out of Scope
| Feature | Deferred to |
|---|---|
impl Aspect in return position | RFC-0037 |
impl Aspect in struct fields / existential types | RFC-0038 |
aspect alias syntax (aspect Sortable = A + B) | RFC-0039 |
| Conditional impls | RFC-0036 |
Resolved Questions
-
Sugar vs distinct form: Pure syntactic sugar.
impl Aspectdesugars to a fresh anonymousTypeParamin a pre-typechecking lowering pass. The typechecker sees only named type parameters. -
Multiple occurrences: Each occurrence is a fresh, independent type variable. Two
impl Comparableparams may be different concrete types. Use a named param to enforce the same type. -
Return position: Not permitted in this RFC. Return-position
impl Aspectrequires careful design around opaque types, inference, and monomorphisation — deferred to RFC-0037. -
Struct fields and type aliases: Not permitted in this RFC. Struct fields require existential type support (
dyn Aspect, RFC-0038). Type aliases that name a bound are the job ofaspectaliases (RFC-0039). -
Error messages: The desugaring pass attaches
source_spellingandsource_spanto each generatedTypeParam. Error messages name the aspect (impl Display), never the internal generated variable name. -
Mixing with named type parameters: Allowed freely. The anonymous parameter is independent of any named parameter in the same signature.
Decision
Outcome: Accepted
Target: v0.7.0
All resolved questions above are the final decisions. Implementation tracked in METEL-57 (AST lowering pass, error message metadata, T0012 enforcement with source spelling).
Coverage Checklist (added 2026-08-19, not part of the original RFC; expanded 2026-08-19: added items 5 and 6, missed in the original pass)
Retroactive breakdown of this RFC's distinct, fixture-testable normative claims, as headed sections for citation purposes only. The document above is unchanged and remains the historical record. Deliberately excludes claims that aren't independently observable from a program's behavior -- implementation strategy, design rationale, or internal architecture discussion belongs in the RFC's own prose, not here.
1. A parameter may use an anonymous impl Aspect bound
A function parameter type may be written as impl Printable, requiring each
argument at that position to implement Printable. The shorthand may also be
used inside the parameter's composite type, such as impl Printable[].
2. Each impl Aspect occurrence is an independent type variable
Two parameters with the same anonymous bound may receive different concrete types, provided each type satisfies that bound. A named type parameter remains the way to require two parameters to have the same concrete type.
3. Anonymous bounds may coexist with named type parameters
A function signature may combine ordinary named generic parameters with one or
more impl Aspect parameter types. An anonymous bound does not constrain an
otherwise unrelated named parameter.
4. An unsatisfied anonymous bound is a type error
Calling a function with an argument whose type does not implement the required aspect is rejected, and the diagnostic identifies the required aspect rather than an implementation-generated type-variable name.
5. Anonymous bounds are rejected in struct-field annotations
impl Aspect remains invalid in a struct-field annotation. Return-position
anonymous bounds are governed by the later RFC-0037 instead.
6. Anonymous bounds are rejected in local binding annotations
impl Aspect remains invalid in a local binding annotation.