@@ -185,6 +185,23 @@ pub struct ConstraintGenerator<'a> {
185185 /// `None` only in unit tests; production passes the map via
186186 /// [`Self::with_module_binding_types`].
187187 module_binding_types : Option < & ' a HashMap < ( FileId , Spur ) , Type > > ,
188+ /// Compile-time type aliases bound by `let` in the current function body
189+ /// (`let P = F();` where `F` returns `type`), pre-resolved by sema before
190+ /// constraint generation. Consulted after `type_subst` when resolving
191+ /// struct-literal type names and `let` annotations, so anonymous-struct
192+ /// aliases route through the same concrete paths as named structs
193+ /// (RUE-170). Like sema's `comptime_type_vars`, the map is flat (not
194+ /// scope-aware). `None` only in unit tests; production passes the map via
195+ /// [`Self::with_comptime_local_types`].
196+ comptime_local_types : Option < & ' a HashMap < Spur , Type > > ,
197+ /// Method signatures registered after the shared `InferenceContext` was
198+ /// built: anonymous-struct methods are registered lazily during comptime
199+ /// evaluation, so they're absent from `methods`. Consulted when a method
200+ /// key misses `methods`, so a call on an anonymous-struct receiver yields
201+ /// its declared return type instead of `<error>` (RUE-164). `None` only
202+ /// in unit tests; production passes the map via
203+ /// [`Self::with_extra_method_sigs`].
204+ extra_method_sigs : Option < & ' a HashMap < ( StructId , Spur ) , MethodSig > > ,
188205 /// Type intern pool for creating pointer and array types during constraint generation.
189206 type_pool : & ' a TypeInternPool ,
190207}
@@ -235,6 +252,8 @@ impl<'a> ConstraintGenerator<'a> {
235252 type_subst,
236253 const_types : None ,
237254 module_binding_types : None ,
255+ comptime_local_types : None ,
256+ extra_method_sigs : None ,
238257 type_pool,
239258 }
240259 }
@@ -256,6 +275,28 @@ impl<'a> ConstraintGenerator<'a> {
256275 self
257276 }
258277
278+ /// Provide pre-resolved comptime type aliases (local name -> concrete
279+ /// type) for struct-literal and `let`-annotation resolution. See the
280+ /// `comptime_local_types` field (RUE-170).
281+ pub fn with_comptime_local_types (
282+ mut self ,
283+ comptime_local_types : & ' a HashMap < Spur , Type > ,
284+ ) -> Self {
285+ self . comptime_local_types = Some ( comptime_local_types) ;
286+ self
287+ }
288+
289+ /// Provide late-registered method signatures (anonymous-struct methods)
290+ /// for method-call resolution. See the `extra_method_sigs` field
291+ /// (RUE-164).
292+ pub fn with_extra_method_sigs (
293+ mut self ,
294+ extra_method_sigs : & ' a HashMap < ( StructId , Spur ) , MethodSig > ,
295+ ) -> Self {
296+ self . extra_method_sigs = Some ( extra_method_sigs) ;
297+ self
298+ }
299+
259300 /// Get the type variables allocated for integer literals.
260301 pub fn int_literal_vars ( & self ) -> & [ TypeVarId ] {
261302 & self . int_literal_vars
@@ -288,6 +329,14 @@ impl<'a> ConstraintGenerator<'a> {
288329 }
289330 }
290331
332+ /// Look up a method signature, falling back to the late-registered
333+ /// (anonymous-struct) signatures when the shared map misses (RUE-164).
334+ fn method_sig ( & self , key : & ( StructId , Spur ) ) -> Option < & ' a MethodSig > {
335+ self . methods
336+ . get ( key)
337+ . or_else ( || self . extra_method_sigs . and_then ( |sigs| sigs. get ( key) ) )
338+ }
339+
291340 /// Resolve a field's declared type on a concrete struct type.
292341 fn field_type_of ( & self , struct_ty : Type , field : Spur ) -> Option < Type > {
293342 let TypeKind :: Struct ( struct_id) = struct_ty. kind ( ) else {
@@ -548,9 +597,18 @@ impl<'a> ConstraintGenerator<'a> {
548597 let init_info = self . generate ( * init, ctx) ;
549598
550599 let var_ty = if let Some ( ty_sym) = type_annotation {
551- // Explicit type annotation - use it and constrain init to match
552- let ty_name = self . interner . resolve ( ty_sym) ;
553- if let Some ( annotated_ty) = self . resolve_type_name ( ty_name) {
600+ // Explicit type annotation - use it and constrain init to
601+ // match. Comptime type aliases (`let P = F(); let p: P =
602+ // ...`) resolve first, mirroring sema's annotation
603+ // validation order (`comptime_type_vars` before the type
604+ // tables); without this the annotation was unenforced and
605+ // any value typechecked against it (RUE-170).
606+ let annotated = self
607+ . comptime_local_types
608+ . and_then ( |aliases| aliases. get ( ty_sym) . copied ( ) )
609+ . map ( |ty| self . type_to_infer ( ty) )
610+ . or_else ( || self . resolve_type_name ( self . interner . resolve ( ty_sym) ) ) ;
611+ if let Some ( annotated_ty) = annotated {
554612 self . add_constraint ( Constraint :: equal (
555613 init_info. ty ,
556614 annotated_ty. clone ( ) ,
@@ -1111,14 +1169,22 @@ impl<'a> ConstraintGenerator<'a> {
11111169 fields_len,
11121170 ..
11131171 } => {
1114- // Check type_subst first (for Self and type parameters in method bodies)
1172+ // Check type_subst first (for Self and type parameters in
1173+ // method bodies), then comptime type aliases (`let P = F();
1174+ // P { ... }`, RUE-170) — mirroring sema, which consults
1175+ // `comptime_type_vars` before the struct table — then named
1176+ // structs.
11151177 let struct_ty = self
11161178 . type_subst
11171179 . and_then ( |subst| subst. get ( type_name) . copied ( ) )
1180+ . or_else ( || {
1181+ self . comptime_local_types
1182+ . and_then ( |aliases| aliases. get ( type_name) . copied ( ) )
1183+ } )
11181184 . or_else ( || self . structs . get ( type_name) . copied ( ) ) ;
11191185
1186+ let fields = self . rir . get_field_inits ( * fields_start, * fields_len) ;
11201187 if let Some ( struct_ty) = struct_ty {
1121- let fields = self . rir . get_field_inits ( * fields_start, * fields_len) ;
11221188 // Constrain each initializer against its field's declared
11231189 // type, so literal initializers are range-checked at the
11241190 // field's width instead of silently wrapping
@@ -1133,6 +1199,14 @@ impl<'a> ConstraintGenerator<'a> {
11331199 }
11341200 InferType :: Concrete ( struct_ty)
11351201 } else {
1202+ // Unknown type name — sema reports the error. Still visit
1203+ // the initializers so every sub-expression gets a type;
1204+ // skipping them left compound initializers (`-1`, `1+2`)
1205+ // with unresolved variables, which sema then reported as
1206+ // an internal compiler error (RUE-170).
1207+ for ( _, value_ref) in fields. iter ( ) {
1208+ self . generate ( * value_ref, ctx) ;
1209+ }
11361210 InferType :: Concrete ( Type :: ERROR )
11371211 }
11381212 }
@@ -1336,9 +1410,11 @@ impl<'a> ConstraintGenerator<'a> {
13361410 }
13371411 InferType :: Concrete ( ty) => {
13381412 if let Some ( struct_id) = ty. as_struct ( ) {
1339- // Use StructId directly for method lookup
1413+ // Use StructId directly for method lookup (falls
1414+ // back to late-registered anonymous-struct
1415+ // signatures, RUE-164)
13401416 let method_key = ( struct_id, * method) ;
1341- if let Some ( method_sig) = self . methods . get ( & method_key) {
1417+ if let Some ( method_sig) = self . method_sig ( & method_key) {
13421418 // Generate constraints for arguments
13431419 for ( arg, param_type) in
13441420 args. iter ( ) . zip ( method_sig. param_types . iter ( ) )
0 commit comments