packages feed

tala-frontend-0.1.0: src/Progmasoft/Tala/Template/Instantiation.hs

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

{- |
Typed declaration instantiation after template application binding.

This pass does not choose overloads, constraints, or a native symbol spelling.
It has one narrow responsibility: replace the semantic template variables in
an already typed declaration tree.  The output is an ordinary type declaration
that the existing Desugarer can lower without learning template syntax.
-}
module Progmasoft.Tala.Template.Instantiation
    ( TemplateInstantiationError (..)
    , instantiateTemplateType
    , instantiateMember
    , instantiateParameter
    , instantiateBlock
    , instantiateStatement
    , instantiateExpression
    , instantiateCallableBody
    , renderTemplateInstantiationError
    ) where

import Progmasoft.Tala.AST
import Progmasoft.Tala.Template.Application

-- | Failure to instantiate a typed template declaration or one of its nodes.
data TemplateInstantiationError
    = -- | The supplied declaration is not a template type.
      ExpectedTemplateTypeDeclaration
    | -- | Binding and declaration identities differ.
      TemplateBindingTargetsDifferentDeclaration SymbolId SymbolId
    | -- | A bound type or value could not be substituted.
      TemplateTypeSubstitutionFailed TemplateApplicationError
    deriving (Eq, Ord, Read, Show)

-- | Close a template type declaration using its previously validated binding.
instantiateTemplateType ::
    TemplateBinding ->
    Declaration ResolvedName Type ->
    Either TemplateInstantiationError (Declaration ResolvedName Type)
instantiateTemplateType binding declaration = case declaration of
    TemplateTypeDeclaration spanValue name annotation _ members
        | resolvedSymbol name /= templateDeclarationSymbol descriptor ->
            Left
                ( TemplateBindingTargetsDifferentDeclaration
                    (templateDeclarationSymbol descriptor)
                    (resolvedSymbol name)
                )
        | otherwise -> do
            closedAnnotation <- instantiateType binding annotation
            closedMembers <- traverse (instantiateMember binding) members
            pure (TypeDeclaration spanValue name closedAnnotation closedMembers)
    _ -> Left ExpectedTemplateTypeDeclaration
    where
        descriptor = templateBindingDeclaration binding

-- | Substitute template variables in one member declaration recursively.
instantiateMember ::
    TemplateBinding ->
    Declaration ResolvedName Type ->
    Either TemplateInstantiationError (Declaration ResolvedName Type)
instantiateMember binding declaration = case declaration of
    FunctionDeclaration spanValue name annotation returnSyntax parameters body isStatic access -> do
        closedAnnotation <- instantiateType binding annotation
        closedParameters <- traverse (instantiateParameter binding) parameters
        closedBody <- instantiateBlock binding body
        pure
            ( FunctionDeclaration
                spanValue
                name
                closedAnnotation
                returnSyntax
                closedParameters
                closedBody
                isStatic
                access
            )
    TypeDeclaration spanValue name annotation members -> do
        closedAnnotation <- instantiateType binding annotation
        closedMembers <- traverse (instantiateMember binding) members
        pure (TypeDeclaration spanValue name closedAnnotation closedMembers)
    TemplateTypeDeclaration {} ->
        -- Nested templates own a distinct parameter environment. Applying the
        -- outer binding blindly would capture same-spelled inner parameters.
        -- Nested instantiation will be selected independently by the planner.
        pure declaration
    EnumDeclaration {} -> pure declaration

-- | Instantiate one parameter annotation while preserving its source syntax.
instantiateParameter ::
    TemplateBinding ->
    Parameter ResolvedName Type ->
    Either TemplateInstantiationError (Parameter ResolvedName Type)
instantiateParameter binding parameter = do
    closedType <- instantiateType binding (parameterAnnotation parameter)
    pure
        ( Parameter
            (parameterSpan parameter)
            (parameterName parameter)
            closedType
            (parameterTypeSyntax parameter)
        )

-- | Instantiate every statement in a block without changing statement order.
instantiateBlock ::
    TemplateBinding ->
    Block ResolvedName Type ->
    Either TemplateInstantiationError (Block ResolvedName Type)
instantiateBlock binding (Block statements) = Block <$> traverse (instantiateStatement binding) statements

-- | Substitute all types nested in one statement and its expressions.
instantiateStatement ::
    TemplateBinding ->
    Statement ResolvedName Type ->
    Either TemplateInstantiationError (Statement ResolvedName Type)
instantiateStatement binding statement = case statement of
    BindingStatement spanValue kind syntax name annotation value -> do
        closedAnnotation <- instantiateType binding annotation
        closedValue <- instantiateExpression binding value
        pure (BindingStatement spanValue kind syntax name closedAnnotation closedValue)
    AssignmentStatement spanValue name annotation value -> do
        closedAnnotation <- instantiateType binding annotation
        closedValue <- instantiateExpression binding value
        pure (AssignmentStatement spanValue name closedAnnotation closedValue)
    ReturnStatement spanValue value ->
        ReturnStatement spanValue <$> traverse (instantiateExpression binding) value
    IfStatement spanValue condition trueBlock falseBlock -> do
        closedCondition <- instantiateExpression binding condition
        closedTrue <- instantiateBlock binding trueBlock
        closedFalse <- traverse (instantiateBlock binding) falseBlock
        pure (IfStatement spanValue closedCondition closedTrue closedFalse)
    WhileStatement spanValue condition body -> do
        closedCondition <- instantiateExpression binding condition
        closedBody <- instantiateBlock binding body
        pure (WhileStatement spanValue closedCondition closedBody)
    DoWhileStatement spanValue body condition -> do
        closedBody <- instantiateBlock binding body
        closedCondition <- instantiateExpression binding condition
        pure (DoWhileStatement spanValue closedBody closedCondition)
    ForStatement spanValue initializer condition updates body -> do
        closedInitializer <- traverse (instantiateStatement binding) initializer
        closedCondition <- traverse (instantiateExpression binding) condition
        closedUpdates <- traverse (instantiateStatement binding) updates
        closedBody <- instantiateBlock binding body
        pure (ForStatement spanValue closedInitializer closedCondition closedUpdates closedBody)
    ForEachStatement spanValue kind syntax name annotation source body -> do
        closedAnnotation <- instantiateType binding annotation
        closedSource <- instantiateExpression binding source
        closedBody <- instantiateBlock binding body
        pure (ForEachStatement spanValue kind syntax name closedAnnotation closedSource closedBody)
    IncrementStatement spanValue name annotation ->
        IncrementStatement spanValue name <$> instantiateType binding annotation
    CompoundAssignmentStatement spanValue operator name annotation value -> do
        closedAnnotation <- instantiateType binding annotation
        closedValue <- instantiateExpression binding value
        pure (CompoundAssignmentStatement spanValue operator name closedAnnotation closedValue)
    DiscardStatement spanValue value ->
        DiscardStatement spanValue <$> instantiateExpression binding value
    BreakStatement spanValue value ->
        BreakStatement spanValue <$> traverse (instantiateExpression binding) value
    ContinueStatement spanValue -> pure (ContinueStatement spanValue)
    GuardStatement spanValue condition block ->
        GuardStatement spanValue <$> instantiateExpression binding condition <*> instantiateBlock binding block
    BlockStatement spanValue block -> BlockStatement spanValue <$> instantiateBlock binding block
    ExpressionStatement spanValue value terminated ->
        ExpressionStatement spanValue <$> instantiateExpression binding value <*> pure terminated

-- | Substitute every type annotation in an expression tree.
instantiateExpression ::
    TemplateBinding ->
    Expression ResolvedName Type ->
    Either TemplateInstantiationError (Expression ResolvedName Type)
instantiateExpression binding expression = case expression of
    NameExpression spanValue name annotation ->
        NameExpression spanValue name <$> instantiateType binding annotation
    LiteralExpression spanValue literal annotation ->
        LiteralExpression spanValue literal <$> instantiateType binding annotation
    MemberAccessExpression spanValue receiver member annotation -> do
        closedReceiver <- instantiateExpression binding receiver
        closedAnnotation <- instantiateType binding annotation
        pure (MemberAccessExpression spanValue closedReceiver member closedAnnotation)
    MethodReferenceExpression spanValue receiver member annotation -> do
        closedReceiver <- instantiateExpression binding receiver
        closedAnnotation <- instantiateType binding annotation
        pure (MethodReferenceExpression spanValue closedReceiver member closedAnnotation)
    CallExpression spanValue callee arguments annotation -> do
        closedCallee <- instantiateExpression binding callee
        closedArguments <- traverse (instantiateExpression binding) arguments
        closedAnnotation <- instantiateType binding annotation
        pure (CallExpression spanValue closedCallee closedArguments closedAnnotation)
    UnaryExpression spanValue operator value annotation -> do
        closedValue <- instantiateExpression binding value
        closedAnnotation <- instantiateType binding annotation
        pure (UnaryExpression spanValue operator closedValue closedAnnotation)
    BinaryExpression spanValue operator left right annotation -> do
        closedLeft <- instantiateExpression binding left
        closedRight <- instantiateExpression binding right
        closedAnnotation <- instantiateType binding annotation
        pure (BinaryExpression spanValue operator closedLeft closedRight closedAnnotation)
    IsPatternExpression spanValue subject patternValue annotation -> do
        closedSubject <- instantiateExpression binding subject
        closedPattern <- instantiatePattern binding patternValue
        closedAnnotation <- instantiateType binding annotation
        pure (IsPatternExpression spanValue closedSubject closedPattern closedAnnotation)
    ConditionalExpression spanValue condition first second annotation -> do
        closedCondition <- instantiateExpression binding condition
        closedFirst <- instantiateExpression binding first
        closedSecond <- instantiateExpression binding second
        closedAnnotation <- instantiateType binding annotation
        pure (ConditionalExpression spanValue closedCondition closedFirst closedSecond closedAnnotation)
    CoalesceExpression spanValue left fallback annotation -> do
        closedLeft <- instantiateExpression binding left
        closedFallback <- instantiateExpression binding fallback
        closedAnnotation <- instantiateType binding annotation
        pure (CoalesceExpression spanValue closedLeft closedFallback closedAnnotation)
    AssignmentExpression spanValue operator name value annotation -> do
        closedValue <- instantiateExpression binding value
        closedAnnotation <- instantiateType binding annotation
        pure (AssignmentExpression spanValue operator name closedValue closedAnnotation)
    IncrementExpression spanValue isPrefix name annotation ->
        IncrementExpression spanValue isPrefix name <$> instantiateType binding annotation
    LoopExpression spanValue loop annotation ->
        LoopExpression spanValue <$> instantiateStatement binding loop <*> instantiateType binding annotation
    BlockExpression spanValue block annotation ->
        BlockExpression spanValue <$> instantiateBlock binding block <*> instantiateType binding annotation
    MatchExpression spanValue subjects arms annotation ->
        MatchExpression spanValue
            <$> traverse (instantiateExpression binding) subjects
            <*> traverse
                (traverseMatchArm (traverseMatchPattern pure (instantiateType binding)) (instantiateExpression binding))
                arms
            <*> instantiateType binding annotation
    CallableExpression spanValue explicit captures parameters body annotation -> do
        closedCaptures <- traverse (instantiateCapture binding) captures
        closedParameters <- traverse (instantiateParameter binding) parameters
        closedBody <- instantiateCallableBody binding body
        closedAnnotation <- instantiateType binding annotation
        pure
            ( CallableExpression
                spanValue
                explicit
                closedCaptures
                closedParameters
                closedBody
                closedAnnotation
            )

instantiatePattern ::
    TemplateBinding ->
    Pattern ResolvedName Type ->
    Either TemplateInstantiationError (Pattern ResolvedName Type)
instantiatePattern binding patternValue = case patternValue of
    WildcardPattern spanValue annotation ->
        WildcardPattern spanValue <$> instantiateType binding annotation
    NullPattern spanValue annotation -> NullPattern spanValue <$> instantiateType binding annotation
    LiteralPattern spanValue literal annotation ->
        LiteralPattern spanValue literal <$> instantiateType binding annotation
    TypePattern spanValue syntax annotation ->
        TypePattern spanValue syntax <$> instantiateType binding annotation
    RelationalPattern spanValue operator literal annotation ->
        RelationalPattern spanValue operator literal <$> instantiateType binding annotation
    NotPattern spanValue nested annotation ->
        NotPattern spanValue
            <$> instantiatePattern binding nested
            <*> instantiateType binding annotation
    AndPattern spanValue left right annotation ->
        AndPattern spanValue
            <$> instantiatePattern binding left
            <*> instantiatePattern binding right
            <*> instantiateType binding annotation
    OrPattern spanValue left right annotation ->
        OrPattern spanValue
            <$> instantiatePattern binding left
            <*> instantiatePattern binding right
            <*> instantiateType binding annotation

instantiateCapture ::
    TemplateBinding ->
    Capture ResolvedName Type ->
    Either TemplateInstantiationError (Capture ResolvedName Type)
instantiateCapture binding capture = do
    closedAnnotation <- instantiateType binding (captureAnnotation capture)
    closedInitializer <- traverse (instantiateExpression binding) (captureInitializer capture)
    pure
        ( Capture
            (captureSpan capture)
            (captureMode capture)
            (captureName capture)
            closedAnnotation
            closedInitializer
        )

-- | Instantiate the expression or block body of a callable.
instantiateCallableBody ::
    TemplateBinding ->
    CallableBody ResolvedName Type ->
    Either TemplateInstantiationError (CallableBody ResolvedName Type)
instantiateCallableBody binding body = case body of
    CallableExpressionBody expression ->
        CallableExpressionBody <$> instantiateExpression binding expression
    CallableBlockBody block -> CallableBlockBody <$> instantiateBlock binding block

instantiateType :: TemplateBinding -> Type -> Either TemplateInstantiationError Type
instantiateType binding valueType = case substituteType binding valueType of
    Right closed -> Right closed
    Left issue -> Left (TemplateTypeSubstitutionFailed issue)

-- | Render a declaration mismatch or template substitution failure.
renderTemplateInstantiationError :: TemplateInstantiationError -> String
renderTemplateInstantiationError issue = case issue of
    ExpectedTemplateTypeDeclaration -> "template instantiation requires a typed template type declaration"
    TemplateBindingTargetsDifferentDeclaration expected actual ->
        "template binding targets symbol "
            ++ show (symbolIdValue expected)
            ++ ", but the declaration has symbol "
            ++ show (symbolIdValue actual)
    TemplateTypeSubstitutionFailed problem -> renderTemplateApplicationError problem