feat: pattern bindings, empty closures, match-as-expr, string interp
Sessions 10–12 from QUALITY_PLAN:
- Pattern payload bindings (Some(value) => value) in bootstrap and selfhost
- Empty-param closures via || (tkPipePipe) with loop/return bodies
- Expression-form match: let x = match …; newline before arms
- f"…" string interpolation desugared to String_Concat + conversions
- Lexer preserves \{ \} for literal braces in f-strings
- Bootstrap fix: f"plain" strips the f prefix after escape processing
- Examples: pattern_matching, closure_control, match_let, string_interp
Selfhost-loop remains binary-identical; all examples and error goldens pass.
This commit is contained in:
+10
-2
@@ -595,11 +595,19 @@ proc emitEnum*(be: var CBackend, name: string, variants: seq[HirEnumVariant]) =
|
||||
be.emitLine(&"typedef union {{")
|
||||
inc be.indent
|
||||
for v in variants:
|
||||
if v.fields.len > 0:
|
||||
# Positional fields
|
||||
if v.fields.len == 1:
|
||||
# Single positional field — flat union member (compat: data.Variant_0)
|
||||
let typ = typeToC(be, v.fields[0])
|
||||
be.emitLine(&"{typ} {v.name}_0;")
|
||||
elif v.fields.len > 1:
|
||||
# Multi positional fields — nested struct so fields don't overlay
|
||||
be.emitLine(&"struct {{")
|
||||
inc be.indent
|
||||
for i, f in v.fields:
|
||||
let typ = typeToC(be, f)
|
||||
be.emitLine(&"{typ} {v.name}_{i};")
|
||||
dec be.indent
|
||||
be.emitLine(&"}} {v.name};")
|
||||
elif v.namedFields.len > 0:
|
||||
# Named fields - generate as struct
|
||||
be.emitLine(&"struct {{")
|
||||
|
||||
+146
-7
@@ -30,6 +30,9 @@ type
|
||||
funcAdapterSigs*: Table[string, Type]
|
||||
## All func types that need BuxFn_* typedefs (including locals)
|
||||
seenFatTypes*: seq[Type]
|
||||
## Pattern-binding names already alloca'd in the current function
|
||||
## (avoids `int v;` twice when two matches bind the same name)
|
||||
patternBoundNames*: HashSet[string]
|
||||
|
||||
proc freshName(ctx: var LowerCtx): string =
|
||||
inc ctx.varCounter
|
||||
@@ -73,6 +76,8 @@ proc patternLiteralNode(pat: Pattern, loc: SourceLocation): HirNode =
|
||||
proc matchAlwaysTrue(loc: SourceLocation): HirNode =
|
||||
hirLit(Token(kind: tkBoolLiteral, text: "true", loc: loc), makeBool(), loc)
|
||||
|
||||
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type
|
||||
|
||||
proc matchPatternCond(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
||||
subjectEnumName: string, subjectHasData: bool,
|
||||
loc: SourceLocation): HirNode =
|
||||
@@ -126,9 +131,99 @@ proc matchPatternCond(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
||||
# Struct/tuple patterns: not yet fully lowered — always-true
|
||||
return nil
|
||||
|
||||
proc matchPatternBindings(ctx: var LowerCtx, subject: HirNode, pattern: Pattern,
|
||||
subjectEnumName: string, subjectHasData: bool,
|
||||
loc: SourceLocation): seq[HirNode] =
|
||||
## Emit alloca+store for identifiers bound by a match pattern.
|
||||
## Enum payload: `Option::Some(value)` → `value = subject.data.Some_0`
|
||||
## Ident catch-all: `x` → `x = subject`
|
||||
result = @[]
|
||||
if pattern == nil: return
|
||||
case pattern.kind
|
||||
of pkIdent:
|
||||
let ty = if subject.typ != nil: subject.typ else: makeUnknown()
|
||||
if pattern.patIdent notin ctx.patternBoundNames:
|
||||
result.add(hirAlloca(pattern.patIdent, ty, loc))
|
||||
ctx.patternBoundNames.incl(pattern.patIdent)
|
||||
result.add(hirStore(hirVar(pattern.patIdent, ty, loc), subject, loc))
|
||||
of pkEnum:
|
||||
if not subjectHasData:
|
||||
return
|
||||
var enumName = ""
|
||||
var variantName = ""
|
||||
if pattern.patEnumPath.len >= 2:
|
||||
enumName = pattern.patEnumPath[0]
|
||||
variantName = pattern.patEnumPath[^1]
|
||||
elif pattern.patEnumPath.len == 1:
|
||||
variantName = pattern.patEnumPath[0]
|
||||
enumName = subjectEnumName
|
||||
if enumName == "" or variantName == "":
|
||||
return
|
||||
# Look up field types from enum declaration
|
||||
var fieldTypes: seq[Type] = @[]
|
||||
var namedFields: seq[tuple[name: string, typ: Type]] = @[]
|
||||
let enumSym = ctx.globalScope.lookup(enumName)
|
||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||
for v in enumSym.decl.declEnumVariants:
|
||||
if v.name == variantName:
|
||||
for f in v.fields:
|
||||
fieldTypes.add(ctx.resolveTypeExpr(f))
|
||||
for nf in v.namedFields:
|
||||
namedFields.add((nf.name, ctx.resolveTypeExpr(nf.ftype)))
|
||||
break
|
||||
let dataType = makeNamed(enumName & "_Data")
|
||||
let dataPtr = HirNode(kind: hFieldPtr, fieldPtrBase: subject, fieldName: "data",
|
||||
typ: makePointer(dataType), loc: loc)
|
||||
let dataLoad = HirNode(kind: hLoad, loadPtr: dataPtr, typ: dataType, loc: loc)
|
||||
# Multi-field positional variants live in a nested struct data.Variant.{Variant_i}
|
||||
# Single-field stay flat as data.Variant_0 for ABI compat.
|
||||
let multiField = fieldTypes.len > 1
|
||||
var payloadBase = dataLoad
|
||||
if multiField:
|
||||
let variantStructTy = makeNamed(variantName)
|
||||
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
||||
typ: makePointer(variantStructTy), loc: loc)
|
||||
payloadBase = HirNode(kind: hLoad, loadPtr: variantPtr, typ: variantStructTy, loc: loc)
|
||||
for i, arg in pattern.patEnumArgs:
|
||||
if arg == nil or arg.kind != pkIdent:
|
||||
continue
|
||||
let fieldName = variantName & "_" & $i
|
||||
let fieldTy = if i < fieldTypes.len: fieldTypes[i] else: makeInt()
|
||||
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: payloadBase, fieldName: fieldName,
|
||||
typ: makePointer(fieldTy), loc: loc)
|
||||
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
||||
if arg.patIdent notin ctx.patternBoundNames:
|
||||
result.add(hirAlloca(arg.patIdent, fieldTy, loc))
|
||||
ctx.patternBoundNames.incl(arg.patIdent)
|
||||
result.add(hirStore(hirVar(arg.patIdent, fieldTy, loc), fieldLoad, loc))
|
||||
for nf in pattern.patEnumNamed:
|
||||
if nf.pattern == nil or nf.pattern.kind != pkIdent:
|
||||
continue
|
||||
var fieldTy = makeInt()
|
||||
for entry in namedFields:
|
||||
if entry.name == nf.name:
|
||||
fieldTy = entry.typ
|
||||
break
|
||||
# Named payload fields live under data.VariantName.name
|
||||
let variantPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: variantName,
|
||||
typ: makePointer(makeNamed(variantName)), loc: loc)
|
||||
let variantLoad = HirNode(kind: hLoad, loadPtr: variantPtr, typ: makeNamed(variantName), loc: loc)
|
||||
let fieldPtr = HirNode(kind: hFieldPtr, fieldPtrBase: variantLoad, fieldName: nf.name,
|
||||
typ: makePointer(fieldTy), loc: loc)
|
||||
let fieldLoad = HirNode(kind: hLoad, loadPtr: fieldPtr, typ: fieldTy, loc: loc)
|
||||
if nf.pattern.patIdent notin ctx.patternBoundNames:
|
||||
result.add(hirAlloca(nf.pattern.patIdent, fieldTy, loc))
|
||||
ctx.patternBoundNames.incl(nf.pattern.patIdent)
|
||||
result.add(hirStore(hirVar(nf.pattern.patIdent, fieldTy, loc), fieldLoad, loc))
|
||||
of pkGuarded:
|
||||
result.add(ctx.matchPatternBindings(subject, pattern.patGuardedInner, subjectEnumName, subjectHasData, loc))
|
||||
else:
|
||||
discard
|
||||
|
||||
proc lowerMatch(ctx: var LowerCtx, subject: HirNode, arms: seq[HirMatchArm], typ: Type, loc: SourceLocation): HirNode =
|
||||
## Lower match expression to a block with if-else chain.
|
||||
## Supports: enum tags, integer/bool/char/string literals, ranges, wildcard/ident.
|
||||
## Supports: enum tags + payload bindings, integer/bool/char/string literals,
|
||||
## ranges, wildcard/ident catch-all.
|
||||
let hasResult = typ != nil and typ.kind != tkVoid and typ.kind != tkUnknown
|
||||
let resultName = ctx.freshName()
|
||||
var stmts: seq[HirNode] = @[]
|
||||
@@ -143,8 +238,8 @@ proc lowerMatch(ctx: var LowerCtx, subject: HirNode, arms: seq[HirMatchArm], typ
|
||||
subjectEnumName = subject.typ.name
|
||||
subjectHasData = ctx.enumHasDataVariants(subjectEnumName)
|
||||
|
||||
proc makeArmBlock(body: HirNode): HirNode =
|
||||
var armStmts: seq[HirNode] = @[]
|
||||
proc makeArmBlock(body: HirNode, bindStmts: seq[HirNode]): HirNode =
|
||||
var armStmts: seq[HirNode] = bindStmts
|
||||
if hasResult:
|
||||
armStmts.add(hirStore(hirVar(resultName, typ, loc), body, loc))
|
||||
elif body != nil:
|
||||
@@ -157,7 +252,8 @@ proc lowerMatch(ctx: var LowerCtx, subject: HirNode, arms: seq[HirMatchArm], typ
|
||||
|
||||
for i in countdown(arms.len - 1, 0):
|
||||
let arm = arms[i]
|
||||
let armBlock = makeArmBlock(arm.body)
|
||||
let binds = matchPatternBindings(ctx, subject, arm.pattern, subjectEnumName, subjectHasData, loc)
|
||||
let armBlock = makeArmBlock(arm.body, binds)
|
||||
let cond = matchPatternCond(ctx, subject, arm.pattern, subjectEnumName, subjectHasData, loc)
|
||||
|
||||
if cond == nil:
|
||||
@@ -202,6 +298,7 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
|
||||
result.funcAdapters = initHashSet[string]()
|
||||
result.funcAdapterSigs = initTable[string, Type]()
|
||||
result.seenFatTypes = @[]
|
||||
result.patternBoundNames = initHashSet[string]()
|
||||
|
||||
proc sanitizeFatPart(s: string): string =
|
||||
result = s.replace("const char*", "cstr").replace("unsigned int", "uint")
|
||||
@@ -244,8 +341,6 @@ proc hirFuncFatTypeName*(typ: Type): string =
|
||||
parts.add("void")
|
||||
return "BuxFn_" & parts.join("_")
|
||||
|
||||
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type
|
||||
|
||||
proc substituteType(ctx: var LowerCtx, te: TypeExpr, subst: Table[string, Type]): Type =
|
||||
if te == nil: return makeUnknown()
|
||||
case te.kind
|
||||
@@ -1314,10 +1409,51 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||
|
||||
of ekMatch:
|
||||
let subject = ctx.lowerExpr(expr.exprMatchSubject)
|
||||
var subjectEnumName = ""
|
||||
var subjectHasData = false
|
||||
if subject.typ != nil and subject.typ.kind == tkNamed:
|
||||
subjectEnumName = subject.typ.name
|
||||
subjectHasData = ctx.enumHasDataVariants(subjectEnumName)
|
||||
# Prefer resolved match type; fall back to function return type when arms
|
||||
# only reference pattern bindings (not yet in varTypeExprs during resolve).
|
||||
var matchTyp = typ
|
||||
if matchTyp == nil or matchTyp.kind == tkUnknown:
|
||||
if ctx.currentFuncRetType != nil and ctx.currentFuncRetType.kind notin {tkVoid, tkUnknown}:
|
||||
matchTyp = ctx.currentFuncRetType
|
||||
var arms: seq[HirMatchArm] = @[]
|
||||
for arm in expr.exprMatchArms:
|
||||
# Register bind types only (do NOT call matchPatternBindings here — that
|
||||
# would mark names as already alloca'd and lowerMatch would skip them).
|
||||
if arm.pattern != nil and arm.pattern.kind == pkEnum and subjectHasData:
|
||||
var enumName = ""
|
||||
var variantName = ""
|
||||
if arm.pattern.patEnumPath.len >= 2:
|
||||
enumName = arm.pattern.patEnumPath[0]
|
||||
variantName = arm.pattern.patEnumPath[^1]
|
||||
elif arm.pattern.patEnumPath.len == 1:
|
||||
variantName = arm.pattern.patEnumPath[0]
|
||||
enumName = subjectEnumName
|
||||
var fieldTypes: seq[Type] = @[]
|
||||
let enumSym = ctx.globalScope.lookup(enumName)
|
||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||
for v in enumSym.decl.declEnumVariants:
|
||||
if v.name == variantName:
|
||||
for f in v.fields:
|
||||
fieldTypes.add(ctx.resolveTypeExpr(f))
|
||||
break
|
||||
for i, arg in arm.pattern.patEnumArgs:
|
||||
if arg != nil and arg.kind == pkIdent:
|
||||
let ft = if i < fieldTypes.len: fieldTypes[i] else: makeInt()
|
||||
ctx.varTypeExprs[arg.patIdent] = typeToTypeExpr(ft)
|
||||
elif arm.pattern != nil and arm.pattern.kind == pkIdent:
|
||||
let ty = if subject.typ != nil: subject.typ else: makeUnknown()
|
||||
ctx.varTypeExprs[arm.pattern.patIdent] = typeToTypeExpr(ty)
|
||||
arms.add(HirMatchArm(pattern: arm.pattern, body: ctx.lowerExpr(arm.body)))
|
||||
return lowerMatch(ctx, subject, arms, typ, loc)
|
||||
# Re-resolve match type now that bindings are registered
|
||||
if matchTyp == nil or matchTyp.kind == tkUnknown:
|
||||
if arms.len > 0 and arms[0].body != nil and arms[0].body.typ != nil:
|
||||
matchTyp = arms[0].body.typ
|
||||
return lowerMatch(ctx, subject, arms, matchTyp, loc)
|
||||
|
||||
of ekSizeOf:
|
||||
let ty = ctx.resolveTypeExpr(expr.exprSizeOfType)
|
||||
@@ -1796,9 +1932,11 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
let oldFuncDecl = ctx.currentFuncDecl
|
||||
let oldFuncRetType = ctx.currentFuncRetType
|
||||
let oldVarTypeExprs = ctx.varTypeExprs
|
||||
let oldPatternBound = ctx.patternBoundNames
|
||||
ctx.currentFuncRetType = retType
|
||||
ctx.currentFuncDecl = decl
|
||||
ctx.varTypeExprs = initTable[string, TypeExpr]() # Clear local vars for new function
|
||||
ctx.patternBoundNames = initHashSet[string]()
|
||||
# Add parameters to varTypeExprs after clearing so they are visible in the body.
|
||||
for p in funcParams:
|
||||
if p.ptype != nil:
|
||||
@@ -1824,6 +1962,7 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
ctx.currentFuncDecl = oldFuncDecl
|
||||
ctx.currentFuncRetType = oldFuncRetType
|
||||
ctx.varTypeExprs = oldVarTypeExprs
|
||||
ctx.patternBoundNames = oldPatternBound
|
||||
|
||||
result = HirFunc(name: funcName, params: params, retType: retType,
|
||||
body: body, isPublic: decl.isPublic)
|
||||
|
||||
@@ -228,6 +228,9 @@ proc scanEscapeSequence(lex: var Lexer): string =
|
||||
of 'r': result = "\r"
|
||||
of 't': result = "\t"
|
||||
of '0': result = "\0"
|
||||
of '{', '}':
|
||||
# Preserve \{ and \} so f"..." interpolation can treat them as literal braces
|
||||
result = "\\" & $c
|
||||
of 'x':
|
||||
var hexVal = ""
|
||||
for _ in 0..<2:
|
||||
|
||||
@@ -496,9 +496,17 @@ proc emitEnumDef(be: var LirCBackend, name: string, variants: seq[HirEnumVariant
|
||||
be.emitLine(&"typedef union {{")
|
||||
be.indent += 1
|
||||
for v in variants:
|
||||
if v.fields.len > 0:
|
||||
if v.fields.len == 1:
|
||||
# Single positional field — flat (compat: data.Variant_0)
|
||||
be.emitLine(&"{typeToCStr(v.fields[0])} {v.name}_0;")
|
||||
elif v.fields.len > 1:
|
||||
# Multi positional — nested struct so fields don't share union storage
|
||||
be.emitLine(&"struct {{")
|
||||
be.indent += 1
|
||||
for i, f in v.fields:
|
||||
be.emitLine(&"{typeToCStr(f)} {v.name}_{i};")
|
||||
be.indent -= 1
|
||||
be.emitLine(&"}} {v.name};")
|
||||
elif v.namedFields.len > 0:
|
||||
be.emitLine(&"struct {{")
|
||||
be.indent += 1
|
||||
|
||||
+22
-1
@@ -463,7 +463,10 @@ proc parseStringInterpolation(p: var Parser, tok: Token): Expr =
|
||||
i += 1
|
||||
texts.add(currentText)
|
||||
if exprs.len == 0:
|
||||
return newLiteralExpr(tok)
|
||||
# f"plain" / f"use \{x\}" with no real {expr} — use processed text (escapes applied)
|
||||
var litTok = tok
|
||||
litTok.text = "\"" & texts[0] & "\""
|
||||
return newLiteralExpr(litTok)
|
||||
return newStringInterpExpr(texts, exprs, tok.loc)
|
||||
|
||||
proc parsePrimary(p: var Parser): Expr =
|
||||
@@ -524,9 +527,16 @@ proc parsePrimary(p: var Parser): Expr =
|
||||
p.structInitAllowed = false
|
||||
let subject = p.parseExpr()
|
||||
p.structInitAllowed = true
|
||||
# Same newline rules as statement-form match (needed for `let x = match ...`)
|
||||
while p.check(tkNewLine):
|
||||
discard p.advance()
|
||||
discard p.expect(tkLBrace, "expected '{' to start match")
|
||||
var arms: seq[MatchArm] = @[]
|
||||
while not p.check(tkRBrace) and not p.isAtEnd:
|
||||
while p.check(tkNewLine):
|
||||
discard p.advance()
|
||||
if p.check(tkRBrace) or p.isAtEnd:
|
||||
break
|
||||
let armLoc = p.currentLoc
|
||||
let pat = p.parsePattern()
|
||||
discard p.expect(tkFatArrow, "expected '=>' in match arm")
|
||||
@@ -581,6 +591,17 @@ proc parsePrimary(p: var Parser): Expr =
|
||||
of tkNull:
|
||||
discard p.advance()
|
||||
return newLiteralExpr(Token(kind: tkNull, text: "null", loc: loc))
|
||||
of tkPipePipe:
|
||||
# Empty-param closure written as `||` — lexer merges two '|' into tkPipePipe.
|
||||
# Disambiguate from logical-or (which only appears mid-expression): as a primary,
|
||||
# `|| -> T { ... }` / `|| { ... }` is always a zero-param closure.
|
||||
discard p.advance() # ||
|
||||
var retTypePP: TypeExpr = nil
|
||||
if p.check(tkArrow):
|
||||
discard p.advance() # ->
|
||||
retTypePP = p.parseType()
|
||||
let bodyPP = p.parseBlock()
|
||||
return Expr(kind: ekClosure, loc: loc, exprClosureParams: @[], exprClosureBody: bodyPP, exprClosureReturnType: retTypePP)
|
||||
of tkPipe:
|
||||
# Closure: |params| -> Ret { body }
|
||||
discard p.advance() # |
|
||||
|
||||
+56
-13
@@ -757,26 +757,67 @@ proc checkTraitBounds(sema: var Sema, funcDecl: Decl, inferredTypes: seq[Type],
|
||||
if not sema.typeImplements(inferredTypes[i], bound):
|
||||
sema.emitError(loc, &"type '{inferredTypes[i].toString}' does not implement trait '{bound}'")
|
||||
|
||||
proc extractPatternBindings(sema: var Sema, pat: Pattern, scope: Scope) =
|
||||
## Add pattern-bound identifiers to scope with unknown type (best-effort)
|
||||
proc extractPatternBindings(sema: var Sema, pat: Pattern, scope: Scope, subjectType: Type = nil) =
|
||||
## Add pattern-bound identifiers to scope. For enum payloads, resolve field types
|
||||
## from the matched enum variant so arm bodies type-check correctly.
|
||||
if pat == nil: return
|
||||
case pat.kind
|
||||
of pkIdent:
|
||||
let sym = Symbol(kind: skVar, name: pat.patIdent, typ: makeUnknown(), isMutable: false)
|
||||
let bindTy = if subjectType != nil and not subjectType.isUnknown: subjectType else: makeUnknown()
|
||||
let sym = Symbol(kind: skVar, name: pat.patIdent, typ: bindTy, isMutable: false)
|
||||
discard scope.define(sym)
|
||||
of pkEnum:
|
||||
for arg in pat.patEnumArgs:
|
||||
sema.extractPatternBindings(arg, scope)
|
||||
# Resolve variant field types from enum declaration
|
||||
var enumName = ""
|
||||
var variantName = ""
|
||||
if pat.patEnumPath.len >= 2:
|
||||
enumName = pat.patEnumPath[0]
|
||||
variantName = pat.patEnumPath[^1]
|
||||
elif pat.patEnumPath.len == 1:
|
||||
variantName = pat.patEnumPath[0]
|
||||
if subjectType != nil and subjectType.kind == tkNamed:
|
||||
enumName = subjectType.name
|
||||
var fieldTypes: seq[Type] = @[]
|
||||
var namedFieldTypes: Table[string, Type]
|
||||
if enumName != "":
|
||||
let enumSym = sema.globalScope.lookup(enumName)
|
||||
if enumSym != nil and enumSym.decl != nil and enumSym.decl.kind == dkEnum:
|
||||
for v in enumSym.decl.declEnumVariants:
|
||||
if v.name == variantName:
|
||||
for f in v.fields:
|
||||
fieldTypes.add(sema.resolveType(f))
|
||||
for nf in v.namedFields:
|
||||
namedFieldTypes[nf.name] = sema.resolveType(nf.ftype)
|
||||
break
|
||||
for i, arg in pat.patEnumArgs:
|
||||
let argTy = if i < fieldTypes.len: fieldTypes[i] else: makeUnknown()
|
||||
if arg.kind == pkIdent:
|
||||
let sym = Symbol(kind: skVar, name: arg.patIdent, typ: argTy, isMutable: false)
|
||||
discard scope.define(sym)
|
||||
else:
|
||||
sema.extractPatternBindings(arg, scope, argTy)
|
||||
for nf in pat.patEnumNamed:
|
||||
sema.extractPatternBindings(nf.pattern, scope)
|
||||
let argTy = if namedFieldTypes.hasKey(nf.name): namedFieldTypes[nf.name] else: makeUnknown()
|
||||
if nf.pattern.kind == pkIdent:
|
||||
let sym = Symbol(kind: skVar, name: nf.pattern.patIdent, typ: argTy, isMutable: false)
|
||||
discard scope.define(sym)
|
||||
else:
|
||||
sema.extractPatternBindings(nf.pattern, scope, argTy)
|
||||
of pkTuple:
|
||||
for elem in pat.patTupleElements:
|
||||
sema.extractPatternBindings(elem, scope)
|
||||
for i, elem in pat.patTupleElements:
|
||||
let elemTy = if subjectType != nil and subjectType.kind == tkTuple and i < subjectType.inner.len:
|
||||
subjectType.inner[i]
|
||||
else: makeUnknown()
|
||||
if elem.kind == pkIdent:
|
||||
let sym = Symbol(kind: skVar, name: elem.patIdent, typ: elemTy, isMutable: false)
|
||||
discard scope.define(sym)
|
||||
else:
|
||||
sema.extractPatternBindings(elem, scope, elemTy)
|
||||
of pkStruct:
|
||||
for f in pat.patStructFields:
|
||||
sema.extractPatternBindings(f.pattern, scope)
|
||||
of pkGuarded:
|
||||
sema.extractPatternBindings(pat.patGuardedInner, scope)
|
||||
sema.extractPatternBindings(pat.patGuardedInner, scope, subjectType)
|
||||
else:
|
||||
discard
|
||||
|
||||
@@ -1381,11 +1422,11 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
||||
lastType = sema.checkStmt(stmt, blockScope)
|
||||
return lastType
|
||||
of ekMatch:
|
||||
discard sema.checkExpr(expr.exprMatchSubject, scope)
|
||||
let subjectType = sema.checkExpr(expr.exprMatchSubject, scope)
|
||||
var resultType = makeUnknown()
|
||||
for arm in expr.exprMatchArms:
|
||||
var armScope = newScope(scope)
|
||||
sema.extractPatternBindings(arm.pattern, armScope)
|
||||
sema.extractPatternBindings(arm.pattern, armScope, subjectType)
|
||||
let armType = sema.checkExpr(arm.body, armScope)
|
||||
if resultType.isUnknown:
|
||||
resultType = armType
|
||||
@@ -1541,9 +1582,11 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
|
||||
discard sema.checkStmt(Stmt(kind: skExpr, loc: stmt.stmtForBody.loc, stmtExpr: Expr(kind: ekBlock, loc: stmt.stmtForBody.loc, exprBlock: stmt.stmtForBody)), forScope)
|
||||
return makeVoid()
|
||||
of skMatch:
|
||||
discard sema.checkExpr(stmt.stmtMatchSubject, scope)
|
||||
let subjectType = sema.checkExpr(stmt.stmtMatchSubject, scope)
|
||||
for arm in stmt.stmtMatchArms:
|
||||
discard sema.checkExpr(arm.body, scope)
|
||||
var armScope = newScope(scope)
|
||||
sema.extractPatternBindings(arm.pattern, armScope, subjectType)
|
||||
discard sema.checkExpr(arm.body, armScope)
|
||||
return makeVoid()
|
||||
of skReturn:
|
||||
if stmt.stmtReturnValue != nil:
|
||||
|
||||
Reference in New Issue
Block a user