@@ -459,6 +459,27 @@ partial def callDunder (inst : Value) (name : String) (args : List Value) : Inte
459459 | _ => some <$> callValueDispatch fn (inst :: args) []
460460 | none => return none
461461
462+ /-- Extended iterValues that handles instance __iter__ via callDunder. -/
463+ partial def iterValuesExt (v : Value) : InterpM (Array Value) := do
464+ match v with
465+ | .instance _ => do
466+ match ← callDunder v "__iter__" [] with
467+ | some iterResult => iterValues iterResult
468+ | none =>
469+ -- Try __getitem__ protocol: iterate with indices 0, 1, 2, ...
470+ let mut items : Array Value := #[]
471+ let mut idx := 0
472+ let mut done := false
473+ while !done do
474+ match ← callDunder v "__getitem__" [.int idx] with
475+ | some item =>
476+ items := items.push item
477+ idx := idx + 1
478+ | none => done := true
479+ if idx == 0 then throwTypeError s! "'{ typeName v} ' object is not iterable"
480+ return items
481+ | _ => iterValues v
482+
462483partial def evalExpr (e : Expr) : InterpM Value := do
463484 match e with
464485 | .name n _ => lookupVariable n
@@ -692,7 +713,7 @@ partial def evalExpr (e : Expr) : InterpM Value := do
692713 return .none
693714 | .yieldFrom iterExpr _ => do
694715 let iterVal ← evalExpr iterExpr
695- let items ← iterValues iterVal
716+ let items ← iterValuesExt iterVal
696717 let st ← get
697718 match st.yieldAccumulator with
698719 | some acc => set { st with yieldAccumulator := some (acc ++ items) }
@@ -706,7 +727,7 @@ partial def evalArgList (args : List Expr) : InterpM (List Value) := do
706727 match arg with
707728 | .starred inner _ => do
708729 let v ← evalExpr inner
709- let items ← iterValues v
730+ let items ← iterValuesExt v
710731 result := result ++ items.toList
711732 | _ => result := result ++ [← evalExpr arg]
712733 return result
@@ -746,10 +767,10 @@ partial def callValueDispatch (callee : Value) (args : List Value)
746767 | [] => pure (Value.none, #[])
747768 | [f] => pure (f, #[])
748769 | [f, init] => do
749- let items ← iterValues init
770+ let items ← iterValuesExt init
750771 let mut pairs : Array (Value × Value) := #[]
751772 for item in items do
752- let kv ← iterValues item
773+ let kv ← iterValuesExt item
753774 if kv.size >= 2 then pairs := pairs.push (kv[0 ]!, kv[1 ]!)
754775 pure (f, pairs)
755776 | _ => throwTypeError "defaultdict() takes at most 2 positional arguments"
@@ -771,7 +792,7 @@ partial def callValueDispatch (callee : Value) (args : List Value)
771792 match args with
772793 | [] => allocList #[]
773794 | [iter] => do
774- let items ← iterValues iter
795+ let items ← iterValuesExt iter
775796 allocList items
776797 | _ => throwTypeError "deque() takes at most 1 argument"
777798 | "operator.itemgetter" => do
@@ -2141,7 +2162,7 @@ partial def evalCompGen (generators : List Comprehension)
21412162 match generators with
21422163 | [] => return acc.push (← body)
21432164 | gen :: rest => do
2144- let items ← iterValues (← evalExpr gen.iter)
2165+ let items ← iterValuesExt (← evalExpr gen.iter)
21452166 let mut result := acc
21462167 for item in items do
21472168 assignToTarget gen.target item
@@ -2157,7 +2178,7 @@ partial def evalDictCompGen (generators : List Comprehension)
21572178 match generators with
21582179 | [] => return acc.push (← body)
21592180 | gen :: rest => do
2160- let items ← iterValues (← evalExpr gen.iter)
2181+ let items ← iterValuesExt (← evalExpr gen.iter)
21612182 let mut result := acc
21622183 for item in items do
21632184 assignToTarget gen.target item
@@ -2514,6 +2535,13 @@ partial def evalSubscriptValue (obj idx : Value) : InterpM Value := do
25142535 match ← callDunder obj "__getitem__" [idx] with
25152536 | some v => return v
25162537 | none => throwTypeError s! "'{ typeName obj} ' object is not subscriptable"
2538+ | .none => return .none -- typing stubs: ClassVar[ int ] , Generic[ T ] , IO[ bytes ] etc.
2539+ | .classObj cref => do
2540+ -- __class_getitem__ support: SomeClass[ T ] calls cls.__class_getitem__ (T)
2541+ let cd ← heapGetClassData cref
2542+ match cd.ns["__class_getitem__" ]? with
2543+ | some fn => callValueDispatch fn [idx] []
2544+ | none => return .none -- default: subscripting a class returns .none (for typing)
25172545 | _ => throwTypeError s! "'{ typeName obj} ' object is not subscriptable"
25182546
25192547-- Assignment target resolution
@@ -2525,7 +2553,7 @@ partial def assignToTarget (target : Expr) (value : Value) : InterpM Unit := do
25252553 let idxVal ← evalExpr idx
25262554 assignSubscriptValue objVal idxVal value
25272555 | .tuple targets _ | .list_ targets _ => do
2528- let items ← iterValues value
2556+ let items ← iterValuesExt value
25292557 -- Check for starred
25302558 let starIdx := targets.findIdx? fun
25312559 | .starred _ _ => true
@@ -2695,9 +2723,11 @@ partial def getBuiltinModule (name : String) : InterpM (Option Value) := do
26952723 "TypeAlias" , "Literal" , "IO" , "Sequence" , "Mapping" ,
26962724 "Iterator" , "Iterable" , "Generator" , "Coroutine" ,
26972725 "Awaitable" , "AsyncIterator" , "AsyncGenerator" ,
2698- "Type" , "Generic" , "TypeVar" , " Annotated" ,
2726+ "Type" , "Generic" , "Annotated" ,
26992727 "overload" , "cast" , "no_type_check" ] do
27002728 ns := ns.insert n .none
2729+ -- TypeVar is callable (returns .none as a stub type variable)
2730+ ns := ns.insert "TypeVar" (.builtin "typing.TypeVar" )
27012731 -- override is a callable identity decorator (not .none)
27022732 ns := ns.insert "override" (.builtin "typing.override" )
27032733 some <$> mkMod ns
@@ -3279,7 +3309,7 @@ partial def execStmt (s : Stmt) : InterpM Unit := do
32793309 if !brokeOut then execStmts orelse
32803310
32813311 | .for_ target iter body orelse _ => do
3282- let items ← iterValues (← evalExpr iter)
3312+ let items ← iterValuesExt (← evalExpr iter)
32833313 let mut brokeOut := false
32843314 for item in items do
32853315 assignToTarget target item
@@ -3724,7 +3754,7 @@ partial def execStmt (s : Stmt) : InterpM Unit := do
37243754 throwImportError s! "cannot import name '{ alias.name} ' from '{ fqName} '"
37253755
37263756 | .asyncFor target iter body orelse _ => do
3727- let items ← iterValues (← evalExpr iter)
3757+ let items ← iterValuesExt (← evalExpr iter)
37283758 let mut brokeOut := false
37293759 for item in items do
37303760 assignToTarget target item
@@ -3975,7 +4005,7 @@ partial def callBoundMethod (receiver : Value) (method : String) (args : List Va
39754005partial def builtinMap (args : List Value) : InterpM Value := do
39764006 match args with
39774007 | [func, iter] => do
3978- let items ← iterValues iter
4008+ let items ← iterValuesExt iter
39794009 let mut result : Array Value := #[]
39804010 for item in items do
39814011 let v ← callValueDispatch func [item] []
@@ -3986,7 +4016,7 @@ partial def builtinMap (args : List Value) : InterpM Value := do
39864016partial def builtinFilter (args : List Value) : InterpM Value := do
39874017 match args with
39884018 | [func, iter] => do
3989- let items ← iterValues iter
4019+ let items ← iterValuesExt iter
39904020 let mut result : Array Value := #[]
39914021 for item in items do
39924022 let keep ← match func with
@@ -4042,14 +4072,14 @@ partial def isBaseModelSubclass (bases : Array Value) : InterpM Bool := do
40424072partial def builtinFunctoolsReduce (args : List Value) : InterpM Value := do
40434073 match args with
40444074 | [func, iter] => do
4045- let items ← iterValues iter
4075+ let items ← iterValuesExt iter
40464076 if items.isEmpty then throwTypeError "reduce() of empty iterable with no initial value"
40474077 let mut acc := items[0 ]!
40484078 for i in [1 :items.size] do
40494079 acc ← callValueDispatch func [acc, items[i]!] []
40504080 return acc
40514081 | [func, iter, initial] => do
4052- let items ← iterValues iter
4082+ let items ← iterValuesExt iter
40534083 let mut acc := initial
40544084 for item in items do
40554085 acc ← callValueDispatch func [acc, item] []
@@ -4070,7 +4100,7 @@ partial def builtinItertoolsAccumulate (args : List Value)
40704100 | some (_, f) => some f
40714101 | none => func
40724102 let initial := kwargs.find? (fun p => p.1 == "initial" ) |>.map (·.2 )
4073- let items ← iterValues iter
4103+ let items ← iterValuesExt iter
40744104 let mut result : Array Value := #[]
40754105 let mut acc : Value := match initial with
40764106 | some v => v
@@ -4295,7 +4325,7 @@ partial def callListMethod (ref : HeapRef) (method : String) (args : List Value)
42954325 | "extend" =>
42964326 match args with
42974327 | [v] => do
4298- let items ← iterValues v
4328+ let items ← iterValuesExt v
42994329 heapSetList ref ((← heapGetList ref) ++ items); return .none
43004330 | _ => throwTypeError "extend() takes exactly one argument"
43014331 | "pop" => do
@@ -4470,7 +4500,7 @@ partial def callStrMethod (s : String) (method : String) (args : List Value)
44704500 | "join" =>
44714501 match args with
44724502 | [iter] => do
4473- let items ← iterValues iter
4503+ let items ← iterValuesExt iter
44744504 let strs ← items.toList.mapM fun v =>
44754505 match v with
44764506 | .str sub => pure sub
@@ -4677,7 +4707,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
46774707 match args with
46784708 | [other] => do
46794709 let a ← heapGetSet ref
4680- let b ← iterValues other
4710+ let b ← iterValuesExt other
46814711 let mut result := a
46824712 for elem in b do
46834713 let mut found := false
@@ -4690,7 +4720,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
46904720 match args with
46914721 | [other] => do
46924722 let a ← heapGetSet ref
4693- let b ← iterValues other
4723+ let b ← iterValuesExt other
46944724 let mut result : Array Value := #[]
46954725 for elem in a do
46964726 let mut found := false
@@ -4703,7 +4733,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
47034733 match args with
47044734 | [other] => do
47054735 let a ← heapGetSet ref
4706- let b ← iterValues other
4736+ let b ← iterValuesExt other
47074737 let mut result : Array Value := #[]
47084738 for elem in a do
47094739 let mut found := false
@@ -4716,7 +4746,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
47164746 match args with
47174747 | [other] => do
47184748 let a ← heapGetSet ref
4719- let b ← iterValues other
4749+ let b ← iterValuesExt other
47204750 let mut result : Array Value := #[]
47214751 for elem in a do
47224752 let mut found := false
@@ -4734,7 +4764,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
47344764 match args with
47354765 | [other] => do
47364766 let a ← heapGetSet ref
4737- let b ← iterValues other
4767+ let b ← iterValuesExt other
47384768 for elem in a do
47394769 let mut found := false
47404770 for otherElem in b do
@@ -4746,7 +4776,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
47464776 match args with
47474777 | [other] => do
47484778 let a ← heapGetSet ref
4749- let b ← iterValues other
4779+ let b ← iterValuesExt other
47504780 for elem in b do
47514781 let mut found := false
47524782 for otherElem in a do
@@ -4758,7 +4788,7 @@ partial def callSetMethod (ref : HeapRef) (method : String) (args : List Value)
47584788 match args with
47594789 | [other] => do
47604790 let a ← heapGetSet ref
4761- let b ← iterValues other
4791+ let b ← iterValuesExt other
47624792 for elem in a do
47634793 for otherElem in b do
47644794 if ← valueEq elem otherElem then return .bool false
@@ -5127,10 +5157,50 @@ partial def applyPydanticModelProcessing (_classVal : Value) (cref : HeapRef)
51275157 nsUpdated := nsUpdated.insert "__pydantic_field_serializers__" (.list fsRef)
51285158 nsUpdated := nsUpdated.insert "__pydantic_model_serializers__" (.list msRef)
51295159 -- 6. Build model_fields dict: {field_name: {"annotation": ..., "default": ...}}
5160+ -- First, collect all raw annotations (own + inherited) for actual type values
5161+ let ownRawAnns ← match cd.ns["__annotations_raw__" ]? with
5162+ | some (.dict ref) => heapGetDict ref
5163+ | _ => pure #[]
5164+ let mut allRawAnns : Array (Value × Value) := #[]
5165+ -- Collect inherited raw annotations from parent classes
5166+ for mroEntry in cd.mro do
5167+ match mroEntry with
5168+ | .classObj mref => do
5169+ if mref == cref then continue
5170+ let mcd ← heapGetClassData mref
5171+ if mcd.name == "BaseModel" then continue
5172+ if mcd.ns["__pydantic_model__" ]? != some (.bool true ) then continue
5173+ match mcd.ns["__annotations_raw__" ]? with
5174+ | some (.dict ref) => do
5175+ let pairs ← heapGetDict ref
5176+ for (k, v) in pairs do
5177+ -- Only add if not already present (child overrides parent)
5178+ if !(allRawAnns.any fun (ek, _) => Value.beq ek k) then
5179+ allRawAnns := allRawAnns.push (k, v)
5180+ | _ => pure ()
5181+ | _ => pure ()
5182+ -- Add own raw annotations (override inherited)
5183+ for (k, v) in ownRawAnns do
5184+ let mut found := false
5185+ for i in [:allRawAnns.size] do
5186+ if Value.beq allRawAnns[i]!.1 k then
5187+ allRawAnns := allRawAnns.set! i (k, v)
5188+ found := true
5189+ break
5190+ if !found then allRawAnns := allRawAnns.push (k, v)
5191+ -- Build the model_fields dict using actual types from raw annotations
51305192 let mut mfPairs : Array (Value × Value) := #[]
51315193 for (name, defVal) in allFields do
51325194 let mut infoPairs : Array (Value × Value) := #[]
5133- infoPairs := infoPairs.push (.str "annotation" , .str "Any" )
5195+ -- Look up actual type from raw annotations
5196+ let annVal ← do
5197+ let mut found : Value := .str "Any"
5198+ for (k, v) in allRawAnns do
5199+ if Value.beq k (.str name) then
5200+ found := v
5201+ break
5202+ pure found
5203+ infoPairs := infoPairs.push (.str "annotation" , annVal)
51345204 match defVal with
51355205 | some v => infoPairs := infoPairs.push (.str "default" , v)
51365206 | none => pure ()
@@ -5140,9 +5210,7 @@ partial def applyPydanticModelProcessing (_classVal : Value) (cref : HeapRef)
51405210 nsUpdated := nsUpdated.insert "model_fields" mfDict
51415211 -- 6b. Build __get_pydantic_core_schema__ field schema map
51425212 -- For each field, check if the annotation type has __get_pydantic_core_schema__
5143- let rawAnns ← match cd.ns["__annotations_raw__" ]? with
5144- | some (.dict ref) => heapGetDict ref
5145- | _ => pure #[]
5213+ let rawAnns := allRawAnns
51465214 let mut fieldSchemaPairs : Array (Value × Value) := #[]
51475215 for (nameV, typeV) in rawAnns do
51485216 match nameV with
0 commit comments