packages feed

tala-frontend-0.1.0: src/Progmasoft/Tala/TypeChecker/Context.hs

-- SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
-- SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1

{- | What the type checker carries from place to place, and how a type
written in source becomes a type.

The catalog is read from the whole source set before any body is checked,
so that a use may precede its declaration. The template context is the
catalog seen from one declaration: its template parameters, the type that
owns it, the type a @return@ must carry and the loops around the place
being checked. A type written in source is resolved in that context,
because the same spelling names a template parameter in one declaration and
a declared type in another.
-}
module Progmasoft.Tala.TypeChecker.Context
    ( TypeEnvironment
    , MethodCandidate (..)
    , TypeCatalog (..)
    , TemplateContext (..)
    , emptyTemplateContext
    , templateContext
    , templateParameterAsArgument
    , syntaxTypeIn
    , syntaxTemplateArgumentIn
    , syntaxTemplateValueIn
    , typeTemplateParameter
    , validateTemplateParameters
    , typeSyntaxProblemsIn
    , templateArgumentProblemsIn
    , templateValueProblemsIn
    ) where

import Progmasoft.Tala.AST
import Progmasoft.Tala.BuiltinTypes
import Progmasoft.Tala.Diagnostic
import Progmasoft.Tala.TemplateValue
import Progmasoft.Tala.TypeChecker.Enums
import Progmasoft.Tala.TypeChecker.Literals
import Progmasoft.Tala.TypeChecker.Loops

-- | The names in scope: each symbol with its type and whether it may be assigned.
type TypeEnvironment = [(SymbolId, (Type, Bool))]

{- | A method that a call may select, with the type that declares it.

The type checker owns a whole source-set catalog before it checks any body.
That permits calls to later-declared classes without making parsing depend
on declaration order or mutating the Renamer's lexical environment.
-}
data MethodCandidate = MethodCandidate
    { candidateOwner :: SymbolId
    , candidateDeclaration :: Declaration ResolvedName ()
    }

-- | What the checker knows of a whole source set before it checks a body.
data TypeCatalog = TypeCatalog
    { catalogTypes :: [(SymbolId, ResolvedName)]
    , catalogMethods :: [MethodCandidate]
    , catalogEnums :: [EnumInfo]
    -- ^ The classic enums of the source set.
    , catalogInferredReturns :: [((SymbolId, SourceSpan), Type)]
    {- ^ The return types inferred for the methods declared with @auto@, by
    the symbol and the place of the declaration. A method that is absent has
    no return type that is known yet.
    -}
    }

{- | The catalog as one declaration sees it.

Type syntax deliberately keeps source spellings. This side environment is
the bridge from those spellings to the SymbolIds assigned by the renamer.
Type and value parameters are separate because @T@ in a type position and
@N@ in @[T; N]@ have different semantic representations.
-}
data TemplateContext = TemplateContext
    { templateTypeNames :: [(Identifier, ResolvedName)]
    , templateValueNames :: [(Identifier, ResolvedName)]
    , templateCatalog :: TypeCatalog
    , templateCurrentType :: Maybe SymbolId
    , contextReturn :: Type
    {- ^ The type a @return@ must carry at the place being checked, or the
    error type where it is not declared. Statements know it already; it is
    kept here for the statements of a block used as a value, which are
    reached through an expression.
    -}
    , contextLoops :: LoopContext
    -- ^ The loops around the place being checked, for the same reason.
    }

-- | The context of a declaration that has no template parameters.
emptyTemplateContext :: TypeCatalog -> Maybe SymbolId -> TemplateContext
emptyTemplateContext catalog owner = TemplateContext [] [] catalog owner ErrorType outsideLoops

{- | Keep type and value parameters in separate lookup tables.
Identical source spelling in the two categories must not collapse their roles.
-}
templateContext :: TypeCatalog -> Maybe SymbolId -> [TemplateParameter ResolvedName annotation] -> TemplateContext
templateContext catalog owner parameters =
    TemplateContext
        [ (resolvedSpelling name, name)
        | parameter <- parameters
        , case templateParameterKind parameter of
            TemplateTypeParameter -> True
            TemplateTemplateParameter _ -> True
            _ -> False
        , let name = templateParameterName parameter
        ]
        [ (resolvedSpelling name, name)
        | parameter <- parameters
        , case templateParameterKind parameter of TemplateValueParameterKind _ -> True; _ -> False
        , let name = templateParameterName parameter
        ]
        catalog
        owner
        ErrorType
        outsideLoops

-- | A template parameter as the argument that stands for itself.
templateParameterAsArgument :: TemplateParameter ResolvedName annotation -> TemplateArgument
templateParameterAsArgument parameter = case templateParameterKind parameter of
    TemplateValueParameterKind _ -> ValueTemplateArgument (TemplateValueParameter (templateParameterName parameter))
    _ -> TypeTemplateArgument (TypeVariable (templateParameterName parameter))

-- | The type a type written in source denotes in a context.
syntaxTypeIn :: TemplateContext -> TypeSyntax -> Type
syntaxTypeIn _ AutoType = ErrorType
syntaxTypeIn context (QualifiedTypeSyntax name arguments) =
    case name of
        QualifiedName [identifier] | Just resolved <- lookup identifier (templateTypeNames context) -> TypeVariable resolved
        _ -> NamedType name (map (syntaxTemplateArgumentIn context) arguments)
syntaxTypeIn context (BuiltinArrayTypeSyntax element) =
    -- `[]T` is a language type, not a public class invented by the compiler.
    -- Its structural spelling keeps that distinction visible through Core
    -- until ownership-aware lowering assigns the final runtime layout.
    NamedType (QualifiedName [Identifier "[]"]) [TypeTemplateArgument (syntaxTypeIn context element)]
syntaxTypeIn context (ArrayTypeSyntax element) =
    NamedType
        (QualifiedName [Identifier "System", Identifier "Array"])
        [TypeTemplateArgument (syntaxTypeIn context element)]
syntaxTypeIn context (FixedArrayTypeSyntax element size) =
    NamedType
        (QualifiedName [Identifier "System", Identifier "Array"])
        [TypeTemplateArgument (syntaxTypeIn context element), ValueTemplateArgument (syntaxTemplateValueIn context size)]
syntaxTypeIn context (DictionaryTypeSyntax key value) =
    NamedType
        (QualifiedName [Identifier "System", Identifier "Dictionary"])
        [TypeTemplateArgument (syntaxTypeIn context key), TypeTemplateArgument (syntaxTypeIn context value)]
syntaxTypeIn context (CallableTypeSyntax parameters result) = FunctionType (map (syntaxTypeIn context) parameters) (syntaxTypeIn context result)
syntaxTypeIn context (ExplicitType identifier@(Identifier name)) = case lookup identifier (templateTypeNames context) of
    Just resolved -> TypeVariable resolved
    Nothing -> case name of
        "String" -> stringType
        "unit" -> unitType
        "void" -> voidType
        _
            | Just info <- enumBySpelling (catalogEnums (templateCatalog context)) identifier -> enumInfoType info
            | otherwise -> maybe (NamedType (QualifiedName [Identifier name]) []) scalarTypeToType (lookupScalar name)
    where
        lookupScalar spelling = lookup spelling [(scalarTypeName scalar, scalar) | scalar <- scalarTypes]

-- | The template argument a written argument denotes in a context.
syntaxTemplateArgumentIn :: TemplateContext -> TemplateArgumentSyntax -> TemplateArgument
syntaxTemplateArgumentIn context argument = case argument of
    TemplateTypeSyntax valueType -> TypeTemplateArgument (syntaxTypeIn context valueType)
    TemplateValueArgumentSyntax value -> ValueTemplateArgument (syntaxTemplateValueIn context value)

-- Parser construction guarantees the expression tree is side-effect free.
-- Exact evaluation here gives every concrete specialization one canonical
-- identity. Invalid arithmetic becomes a sentinel and is diagnosed by the
-- type-syntax validation pass before Core can be emitted.

{- | Canonicalize a template argument before it contributes to specialization identity.
The fallback value is only a recovery sentinel; validation reports the original
invalid expression before a specialization plan is emitted.
-}
syntaxTemplateValueIn :: TemplateContext -> TemplateValueSyntax -> TemplateValue
syntaxTemplateValueIn context value = case value of
    TemplateNameSyntax _ (QualifiedName [identifier])
        | Just resolved <- lookup identifier (templateValueNames context) -> TemplateValueParameter resolved
    _ -> case evaluateTemplateValue value of
        Right result -> result
        _ -> IntegerTemplateValue 0

-- | A template parameter with the type of its values.
typeTemplateParameter :: TemplateContext -> TemplateParameter ResolvedName () -> TemplateParameter ResolvedName Type
typeTemplateParameter context parameter =
    TemplateParameter
        (templateParameterSpan parameter)
        (templateParameterName parameter)
        annotation
        (templateParameterKind parameter)
        (templateParameterIsPack parameter)
        (templateParameterDefault parameter)
    where
        annotation = case templateParameterKind parameter of
            TemplateValueParameterKind valueType -> syntaxTypeIn context valueType
            _ -> TypeVariable (templateParameterName parameter)

-- | The problems of the template parameters of a declaration.
validateTemplateParameters :: TemplateContext -> [TemplateParameter ResolvedName ()] -> [Diagnostic]
validateTemplateParameters context = concatMap validate
    where
        validate parameter =
            kindProblems parameter
                ++ defaultProblems parameter
                ++ packDefaultProblems parameter
        kindProblems parameter = case templateParameterKind parameter of
            TemplateTypeParameter -> []
            TemplateValueParameterKind valueType -> typeSyntaxProblemsIn context valueType
            TemplateTemplateParameter shapes -> concatMap shapeProblems shapes
        shapeProblems shape = case templateParameterShapeKind shape of
            TemplateTypeParameterShape -> []
            TemplateValueParameterShape valueType -> typeSyntaxProblemsIn context valueType
            TemplateTemplateParameterShape shapes -> concatMap shapeProblems shapes
        defaultProblems parameter = case templateParameterDefault parameter of
            Nothing -> []
            Just (TemplateTypeDefault valueType) -> typeSyntaxProblemsIn context valueType
            Just (TemplateValueDefault value) -> templateValueProblemsIn context "TAT0020" value
        packDefaultProblems parameter
            | templateParameterIsPack parameter
            , Just _ <- templateParameterDefault parameter =
                [problem (templateParameterSpan parameter) "TAT0019" "a template parameter pack cannot have a default"]
            | otherwise = []

-- | The problems of a type written in source.
typeSyntaxProblemsIn :: TemplateContext -> TypeSyntax -> [Diagnostic]
typeSyntaxProblemsIn context syntax = case syntax of
    ExplicitType _ -> []
    AutoType -> []
    BuiltinArrayTypeSyntax element -> typeSyntaxProblemsIn context element
    ArrayTypeSyntax element -> typeSyntaxProblemsIn context element
    DictionaryTypeSyntax key value -> typeSyntaxProblemsIn context key ++ typeSyntaxProblemsIn context value
    CallableTypeSyntax parameters result -> concatMap (typeSyntaxProblemsIn context) parameters ++ typeSyntaxProblemsIn context result
    QualifiedTypeSyntax _ arguments -> concatMap (templateArgumentProblemsIn context) arguments
    FixedArrayTypeSyntax element size ->
        typeSyntaxProblemsIn context element
            ++ case syntaxTemplateValueIn context size of
                TemplateValueParameter _ -> []
                _ -> case evaluateFixedArraySize size of
                    Left issue -> [problem (templateValueSyntaxSpan size) "TAT0016" (renderTemplateValueError issue)]
                    Right _ -> []

-- | The problems of a template argument written in source.
templateArgumentProblemsIn :: TemplateContext -> TemplateArgumentSyntax -> [Diagnostic]
templateArgumentProblemsIn context argument = case argument of
    TemplateTypeSyntax valueType -> typeSyntaxProblemsIn context valueType
    TemplateValueArgumentSyntax value -> templateValueProblemsIn context "TAT0017" value

-- | The problems of a template value written in source, reported with the given code.
templateValueProblemsIn :: TemplateContext -> String -> TemplateValueSyntax -> [Diagnostic]
templateValueProblemsIn context code value = case syntaxTemplateValueIn context value of
    TemplateValueParameter _ -> []
    _ -> case evaluateTemplateValue value of
        Left issue -> [problem (templateValueSyntaxSpan value) code (renderTemplateValueError issue)]
        Right _ -> []