@@ -731,7 +731,33 @@ export const CORE_LIBRARY: SymbolDefinitions[] = [
731731 if ( op1 . string && asSmallInteger ( op2 ) !== null ) return 'integer' ;
732732 if ( op1 . isIndexedCollection )
733733 return collectionElementType ( op1 . type . type ) ?? 'any' ;
734- if ( op1 . symbol ) return 'symbol' ;
734+
735+ // Check if the symbol is declared as a collection type
736+ if ( op1 . symbol ) {
737+ const eltType = collectionElementType ( op1 . type . type ) ;
738+ if ( eltType ) return eltType ;
739+ }
740+
741+ // For symbol bases with complex subscripts (like a_{n+1}), return 'unknown'
742+ // to allow type inference in arithmetic contexts. Simple subscripts
743+ // (like a_n) are converted to compound symbols during canonicalization
744+ // and won't reach this type function.
745+ if ( op1 . symbol ) {
746+ // Check if this would become a compound symbol (simple subscript)
747+ const sub =
748+ op2 . string ?? op2 . symbol ?? asSmallInteger ( op2 ) ?. toString ( ) ;
749+ if ( sub ) return 'symbol' ;
750+ // Check for InvisibleOperator of symbols/numbers (also becomes compound symbol)
751+ if ( op2 . operator === 'InvisibleOperator' && op2 . ops ) {
752+ const parts = op2 . ops . map (
753+ ( x ) => x . symbol ?? asSmallInteger ( x ) ?. toString ( )
754+ ) ;
755+ if ( parts . every ( ( p ) => p !== undefined && p !== null ) )
756+ return 'symbol' ;
757+ }
758+ // Complex subscript - return 'unknown' to allow numeric inference
759+ return 'unknown' ;
760+ }
735761 return 'expression' ;
736762 } ,
737763
@@ -754,9 +780,21 @@ export const CORE_LIBRARY: SymbolDefinitions[] = [
754780 ] ) ;
755781 }
756782
757- // Is it a collection?
783+ // Is it a collection expression (like a list literal) ?
758784 if ( op1 . isIndexedCollection ) return ce . _fn ( 'At' , [ op1 , op2 . canonical ] ) ;
759785
786+ // Is it a symbol declared as a collection type?
787+ // If so, convert to At() for indexing
788+ if ( op1 . symbol && collectionElementType ( op1 . type . type ) ) {
789+ // For multi-index subscripts (Sequence/Tuple), pass each index as separate arg
790+ if (
791+ ( op2 . operator === 'Sequence' || op2 . operator === 'Tuple' ) &&
792+ op2 . ops
793+ )
794+ return ce . _fn ( 'At' , [ op1 , ...op2 . ops . map ( ( x ) => x . canonical ) ] ) ;
795+ return ce . _fn ( 'At' , [ op1 , op2 . canonical ] ) ;
796+ }
797+
760798 // Is it a compound symbol `x_\operatorname{max}`, `\mu_0`
761799 if ( op1 . symbol ) {
762800 const sub =
0 commit comments