Phase 8.2.4: Lifetime syntax ('a) support
- Lexer tokenizes 'a as tkLifetime - Parser accepts lifetimes in type params <'a> and reference types &'a T / &'a mut T - AST TypeExpr has refLifetime field for tekRef/tekMutRef - TypeParam gains isLifetime flag for generic monomorphization - Sema infers lifetime params as no-op dummy values; unwraps pointee types for params inside refs - HIR lower skips lifetime params when generating mangled names and substitution tables - &mut expr syntax supported in parser (consumed as part of unary &)
This commit is contained in:
@@ -47,6 +47,7 @@ type
|
||||
sliceSize*: Expr ## nil for unsized slices T[]
|
||||
of tekOwn, tekPointer, tekRef, tekMutRef:
|
||||
pointerPointee*: TypeExpr
|
||||
refLifetime*: string ## only meaningful for tekRef/tekMutRef
|
||||
of tekDynRef:
|
||||
dynInterface*: string
|
||||
of tekTuple:
|
||||
@@ -300,6 +301,7 @@ type
|
||||
TypeParam* = object
|
||||
name*: string
|
||||
bounds*: seq[string] ## e.g. ["Comparable"] for <T: Comparable>
|
||||
isLifetime*: bool ## true for lifetime params like 'a
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Declarations
|
||||
|
||||
+34
-6
@@ -564,6 +564,23 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||
else: discard
|
||||
if calleeName != "":
|
||||
var typeSuffix = ""
|
||||
var typeArgIdx = 0
|
||||
if ctx.genericFuncs.hasKey(calleeName):
|
||||
let genericDecl = ctx.genericFuncs[calleeName]
|
||||
for j, tp in genericDecl.declFuncTypeParams:
|
||||
if tp.isLifetime: continue
|
||||
if typeArgIdx > 0:
|
||||
typeSuffix.add("_")
|
||||
if j < expr.exprCallInferredTypeArgs.len:
|
||||
let targ = expr.exprCallInferredTypeArgs[j]
|
||||
if targ.kind == tekNamed:
|
||||
typeSuffix.add(targ.typeName)
|
||||
else:
|
||||
typeSuffix.add("unknown")
|
||||
else:
|
||||
typeSuffix.add("unknown")
|
||||
inc(typeArgIdx)
|
||||
else:
|
||||
for i, targ in expr.exprCallInferredTypeArgs:
|
||||
if i > 0:
|
||||
typeSuffix.add("_")
|
||||
@@ -1072,14 +1089,17 @@ proc generateMethodInstance(ctx: var LowerCtx, baseMethodName: string, typeArgs:
|
||||
return baseMethodName
|
||||
var subst = initTable[string, Type]()
|
||||
var typeSuffix = ""
|
||||
var typeArgIdx = 0
|
||||
for i, tp in genericDecl.declFuncTypeParams:
|
||||
if i > 0: typeSuffix.add("_")
|
||||
if i < typeArgs.len:
|
||||
let argType = ctx.resolveTypeExpr(typeArgs[i])
|
||||
if tp.isLifetime: continue
|
||||
if typeArgIdx > 0: typeSuffix.add("_")
|
||||
if typeArgIdx < typeArgs.len:
|
||||
let argType = ctx.resolveTypeExpr(typeArgs[typeArgIdx])
|
||||
subst[tp.name] = argType
|
||||
typeSuffix.add(argType.toString)
|
||||
else:
|
||||
typeSuffix.add("unknown")
|
||||
inc(typeArgIdx)
|
||||
let mangledName = baseMethodName & "_" & typeSuffix
|
||||
if not ctx.generatedFuncInsts.hasKey(mangledName):
|
||||
var specDecl = Decl(
|
||||
@@ -1225,21 +1245,29 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
for inst in instantiations:
|
||||
let baseName = inst.name
|
||||
if ctx.genericFuncs.hasKey(baseName):
|
||||
let genericDecl = ctx.genericFuncs[baseName]
|
||||
var typeSuffix = ""
|
||||
for i, targ in inst.typeArgs:
|
||||
if i > 0: typeSuffix.add("_")
|
||||
var nonLifetimeIdx = 0
|
||||
for j, tp in genericDecl.declFuncTypeParams:
|
||||
if tp.isLifetime: continue
|
||||
if nonLifetimeIdx > 0: typeSuffix.add("_")
|
||||
if j < inst.typeArgs.len:
|
||||
let targ = inst.typeArgs[j]
|
||||
if targ.kind == tekNamed:
|
||||
typeSuffix.add(targ.typeName)
|
||||
else:
|
||||
typeSuffix.add("unknown")
|
||||
else:
|
||||
typeSuffix.add("unknown")
|
||||
inc(nonLifetimeIdx)
|
||||
let mangledName = baseName & "_" & typeSuffix
|
||||
if not generated.hasKey(mangledName):
|
||||
# Generate specialized version
|
||||
let genericDecl = ctx.genericFuncs[baseName]
|
||||
|
||||
# Build type substitution table
|
||||
var subst = initTable[string, Type]()
|
||||
for j, tp in genericDecl.declFuncTypeParams:
|
||||
if tp.isLifetime: continue
|
||||
if j < inst.typeArgs.len:
|
||||
let targ = inst.typeArgs[j]
|
||||
if targ.kind == tekNamed:
|
||||
|
||||
@@ -542,6 +542,24 @@ proc nextToken(lex: var Lexer): Token =
|
||||
return lex.scanChar(startLoc, 3)
|
||||
|
||||
if c == '\'':
|
||||
# Check if this is a lifetime (e.g., 'a) or char literal (e.g., 'a')
|
||||
# Lifetime: ' followed by identifier chars, no closing '
|
||||
# Char literal: ' followed by one char/escape, then closing '
|
||||
let afterQuote = lex.peek(1)
|
||||
if isIdentStart(afterQuote):
|
||||
# Could be lifetime or char literal like 'x'
|
||||
# If next char after ident start is NOT ', it's a lifetime
|
||||
# (for char literals, the char/escape is consumed and then ')
|
||||
# Simple heuristic: if peek(2) is ', it's a char literal; else lifetime
|
||||
if lex.peek(2) == '\'':
|
||||
return lex.scanChar(startLoc, 0)
|
||||
else:
|
||||
# Lifetime: consume ' and then identifier chars
|
||||
discard lex.advance() # '
|
||||
while not lex.isAtEnd() and isIdentChar(lex.peek()):
|
||||
discard lex.advance()
|
||||
return lex.makeToken(tkLifetime, startLoc, startPos)
|
||||
else:
|
||||
return lex.scanChar(startLoc, 0)
|
||||
|
||||
if isIdentStart(c):
|
||||
|
||||
+19
-6
@@ -209,20 +209,23 @@ proc parseBaseType(p: var Parser): TypeExpr =
|
||||
return TypeExpr(kind: tekNamed, loc: loc, typeName: name)
|
||||
of tkOwn:
|
||||
discard p.advance()
|
||||
return TypeExpr(kind: tekOwn, loc: loc, pointerPointee: p.parseBaseType())
|
||||
return TypeExpr(kind: tekOwn, loc: loc, pointerPointee: p.parseBaseType(), refLifetime: "")
|
||||
of tkStar:
|
||||
discard p.advance()
|
||||
return TypeExpr(kind: tekPointer, loc: loc, pointerPointee: p.parseBaseType())
|
||||
return TypeExpr(kind: tekPointer, loc: loc, pointerPointee: p.parseBaseType(), refLifetime: "")
|
||||
of tkAmp:
|
||||
discard p.advance()
|
||||
var lt = ""
|
||||
if p.check(tkLifetime):
|
||||
lt = p.advance().text
|
||||
if p.check(tkMut):
|
||||
discard p.advance()
|
||||
return TypeExpr(kind: tekMutRef, loc: loc, pointerPointee: p.parseBaseType())
|
||||
return TypeExpr(kind: tekMutRef, loc: loc, refLifetime: lt, pointerPointee: p.parseBaseType())
|
||||
if p.check(tkDyn):
|
||||
discard p.advance()
|
||||
let ifaceName = p.expect(tkIdent, "expected interface name after 'dyn'").text
|
||||
return TypeExpr(kind: tekDynRef, loc: loc, dynInterface: ifaceName)
|
||||
return TypeExpr(kind: tekRef, loc: loc, pointerPointee: p.parseBaseType())
|
||||
return TypeExpr(kind: tekRef, loc: loc, refLifetime: lt, pointerPointee: p.parseBaseType())
|
||||
of tkLParen:
|
||||
discard p.advance()
|
||||
var elems: seq[TypeExpr] = @[]
|
||||
@@ -595,6 +598,8 @@ proc parseUnary(p: var Parser): Expr =
|
||||
case p.peek()
|
||||
of tkBang, tkMinus, tkTilde, tkStar, tkAmp:
|
||||
let op = p.advance().kind
|
||||
if op == tkAmp and p.check(tkMut):
|
||||
discard p.advance() # mut
|
||||
let operand = p.parseUnary()
|
||||
return Expr(kind: ekUnary, loc: loc, exprUnaryOp: op, exprUnaryOperand: operand)
|
||||
of tkPlusPlus, tkMinusMinus:
|
||||
@@ -944,7 +949,15 @@ proc parseTypeParams(p: var Parser): seq[TypeParam] =
|
||||
if p.check(tkLt):
|
||||
discard p.advance()
|
||||
while not p.check(tkGt) and not p.isAtEnd:
|
||||
let name = p.expect(tkIdent, "expected type parameter name").text
|
||||
var name = ""
|
||||
var isLifetime = false
|
||||
if p.check(tkIdent):
|
||||
name = p.advance().text
|
||||
elif p.check(tkLifetime):
|
||||
name = p.advance().text
|
||||
isLifetime = true
|
||||
else:
|
||||
discard p.expect(tkIdent, "expected type parameter name")
|
||||
var bounds: seq[string] = @[]
|
||||
if p.check(tkColon):
|
||||
discard p.advance()
|
||||
@@ -960,7 +973,7 @@ proc parseTypeParams(p: var Parser): seq[TypeParam] =
|
||||
else:
|
||||
break
|
||||
bounds.add(boundName)
|
||||
result.add(TypeParam(name: name, bounds: bounds))
|
||||
result.add(TypeParam(name: name, bounds: bounds, isLifetime: isLifetime))
|
||||
if p.check(tkComma):
|
||||
discard p.advance()
|
||||
discard p.expect(tkGt, "expected '>' to close type parameters")
|
||||
|
||||
+29
-7
@@ -115,7 +115,10 @@ proc typeExprReferencesTypeParam(te: TypeExpr, name: string): bool =
|
||||
return false
|
||||
of tekSlice:
|
||||
return typeExprReferencesTypeParam(te.sliceElement, name)
|
||||
of tekOwn, tekPointer, tekRef, tekMutRef:
|
||||
of tekOwn, tekPointer:
|
||||
return typeExprReferencesTypeParam(te.pointerPointee, name)
|
||||
of tekRef, tekMutRef:
|
||||
if te.refLifetime == name: return true
|
||||
return typeExprReferencesTypeParam(te.pointerPointee, name)
|
||||
of tekDynRef:
|
||||
return false
|
||||
@@ -145,17 +148,17 @@ proc typeToTypeExpr(t: Type): TypeExpr =
|
||||
of tkNamed: TypeExpr(kind: tekNamed, typeName: t.name)
|
||||
of tkPointer:
|
||||
if t.inner.len > 0:
|
||||
TypeExpr(kind: tekPointer, pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
TypeExpr(kind: tekPointer, refLifetime: "", pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
else:
|
||||
TypeExpr(kind: tekNamed, typeName: "void")
|
||||
of tkRef:
|
||||
if t.inner.len > 0:
|
||||
TypeExpr(kind: tekRef, pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
TypeExpr(kind: tekRef, refLifetime: "", pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
else:
|
||||
TypeExpr(kind: tekNamed, typeName: "void")
|
||||
of tkMutRef:
|
||||
if t.inner.len > 0:
|
||||
TypeExpr(kind: tekMutRef, pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
TypeExpr(kind: tekMutRef, refLifetime: "", pointerPointee: typeToTypeExpr(t.inner[0]))
|
||||
else:
|
||||
TypeExpr(kind: tekNamed, typeName: "void")
|
||||
of tkVoid: TypeExpr(kind: tekNamed, typeName: "void")
|
||||
@@ -168,6 +171,19 @@ proc inferTypeArgs(sema: var Sema, funcDecl: Decl, argTypes: seq[Type],
|
||||
result = @[]
|
||||
for tp in funcDecl.declFuncTypeParams:
|
||||
let tpName = tp.name
|
||||
# Lifetime params are inferred from ref lifetime positions
|
||||
if tp.isLifetime:
|
||||
var found = false
|
||||
for i, param in funcDecl.declFuncParams:
|
||||
if i >= argTypes.len: break
|
||||
if param.ptype.kind in {tekRef, tekMutRef} and param.ptype.refLifetime == tpName:
|
||||
found = true
|
||||
break
|
||||
if found:
|
||||
result.add(TypeExpr(kind: tekNamed, typeName: "lifetime"))
|
||||
continue
|
||||
# If not found in refs, treat as uninferrable
|
||||
return @[]
|
||||
var inferred: Type = nil
|
||||
for i, param in funcDecl.declFuncParams:
|
||||
if i >= argTypes.len: break
|
||||
@@ -176,9 +192,15 @@ proc inferTypeArgs(sema: var Sema, funcDecl: Decl, argTypes: seq[Type],
|
||||
if param.ptype.kind in {tekOwn, tekPointer}:
|
||||
continue
|
||||
if typeExprReferencesTypeParam(param.ptype, tpName):
|
||||
var argType = argTypes[i]
|
||||
# If type param is inside a ref/pointer pointee, unwrap the arg type
|
||||
if param.ptype.kind in {tekRef, tekMutRef, tekPointer} and
|
||||
typeExprReferencesTypeParam(param.ptype.pointerPointee, tpName) and
|
||||
argType.isPointer and argType.inner.len > 0:
|
||||
argType = argType.inner[0]
|
||||
if inferred == nil:
|
||||
inferred = argTypes[i]
|
||||
elif inferred != argTypes[i]:
|
||||
inferred = argType
|
||||
elif inferred != argType:
|
||||
# Check if one is assignable to the other (wider type wins)
|
||||
if argTypes[i].isAssignableTo(inferred):
|
||||
discard # inferred stays the same
|
||||
@@ -187,7 +209,7 @@ proc inferTypeArgs(sema: var Sema, funcDecl: Decl, argTypes: seq[Type],
|
||||
else:
|
||||
sema.emitError(loc,
|
||||
&"conflicting types for type parameter '{tpName}': " &
|
||||
&"{inferred.toString} vs {argTypes[i].toString}")
|
||||
&"{inferred.toString} vs {argType.toString}")
|
||||
return @[]
|
||||
if inferred != nil and not inferred.isUnknown:
|
||||
result.add(typeToTypeExpr(inferred))
|
||||
|
||||
@@ -58,6 +58,7 @@ type
|
||||
tkStaticAssert # static_assert
|
||||
tkComptime # comptime
|
||||
tkDyn # dyn
|
||||
tkLifetime # 'a (lifetime parameter)
|
||||
|
||||
##Punctuation
|
||||
tkLParen # (
|
||||
@@ -265,6 +266,7 @@ proc tokenKindName*(kind: TokenKind): string =
|
||||
of tkStaticAssert: "'static_assert'"
|
||||
of tkComptime: "'comptime'"
|
||||
of tkDyn: "'dyn'"
|
||||
of tkLifetime: "lifetime"
|
||||
of tkLParen: "'('"
|
||||
of tkRParen: "')'"
|
||||
of tkLBrace: "'{'"
|
||||
|
||||
Reference in New Issue
Block a user