borrow checking (#15282)
* refactoring: move procs to typeallowed.nim * frontend preparations for first class openArray support * prepare the code generator for first class openArray * code generation for first class openArray; WIP * code generation for open arrays, progress * added isViewType proc * preparations for borrow checking * added borrow checking to the front end
This commit is contained in:
parent
c49b88163c
commit
10988d4840
18 changed files with 554 additions and 311 deletions
228
compiler/typeallowed.nim
Normal file
228
compiler/typeallowed.nim
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2020 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains 'typeAllowed' and friends which check
|
||||
## for invalid types like 'openArray[var int]'.
|
||||
|
||||
import
|
||||
intsets, ast, renderer, options, semdata, types
|
||||
|
||||
type
|
||||
TTypeAllowedFlag* = enum
|
||||
taField,
|
||||
taHeap,
|
||||
taConcept,
|
||||
taIsOpenArray,
|
||||
taNoUntyped
|
||||
taIsTemplateOrMacro
|
||||
taProcContextIsNotMacro
|
||||
|
||||
TTypeAllowedFlags* = set[TTypeAllowedFlag]
|
||||
|
||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind;
|
||||
c: PContext; flags: TTypeAllowedFlags = {}): PType
|
||||
|
||||
proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
|
||||
c: PContext; flags: TTypeAllowedFlags = {}): PType =
|
||||
if n != nil:
|
||||
result = typeAllowedAux(marker, n.typ, kind, c, flags)
|
||||
if result == nil:
|
||||
case n.kind
|
||||
of nkNone..nkNilLit:
|
||||
discard
|
||||
else:
|
||||
#if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
|
||||
# return n[0].typ
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
result = typeAllowedNode(marker, it, kind, c, flags)
|
||||
if result != nil: break
|
||||
|
||||
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
c: PContext; flags: TTypeAllowedFlags = {}): PType =
|
||||
assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
|
||||
# if we have already checked the type, return true, because we stop the
|
||||
# evaluation if something is wrong:
|
||||
result = nil
|
||||
if typ == nil: return nil
|
||||
if containsOrIncl(marker, typ.id): return nil
|
||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
if kind in {skProc, skFunc, skConst} and (views notin c.features):
|
||||
result = t
|
||||
elif t.kind == tyLent and kind != skResult:
|
||||
result = t
|
||||
else:
|
||||
var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar, tyLent:
|
||||
if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
|
||||
of tyOpenArray:
|
||||
if (kind != skParam and views notin c.features) or taIsOpenArray in flags: result = t
|
||||
else: result = typeAllowedAux(marker, t2[0], kind, c, flags+{taIsOpenArray})
|
||||
of tyUncheckedArray:
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t2[0], kind, c, flags)
|
||||
else:
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, c, flags)
|
||||
of tyProc:
|
||||
if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
|
||||
result = t
|
||||
else:
|
||||
if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
|
||||
# only closure iterators may be assigned to anything.
|
||||
result = t
|
||||
let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
|
||||
for i in 1..<t.len:
|
||||
if result != nil: break
|
||||
result = typeAllowedAux(marker, t[i], skParam, c, f-{taIsOpenArray})
|
||||
if result.isNil and t[0] != nil:
|
||||
result = typeAllowedAux(marker, t[0], skResult, c, flags)
|
||||
of tyTypeDesc:
|
||||
if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
|
||||
result = t
|
||||
else:
|
||||
# XXX: This is still a horrible idea...
|
||||
result = nil
|
||||
of tyUntyped, tyTyped:
|
||||
if kind notin {skParam, skResult} or taNoUntyped in flags: result = t
|
||||
of tyStatic:
|
||||
if kind notin {skParam}: result = t
|
||||
of tyVoid:
|
||||
if taField notin flags: result = t
|
||||
of tyTypeClasses:
|
||||
if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
|
||||
discard
|
||||
elif t.isResolvedUserTypeClass:
|
||||
result = typeAllowedAux(marker, t.lastSon, kind, c, flags)
|
||||
elif kind notin {skParam, skResult}:
|
||||
result = t
|
||||
of tyGenericBody, tyGenericParam, tyGenericInvocation,
|
||||
tyNone, tyForward, tyFromExpr:
|
||||
result = t
|
||||
of tyNil:
|
||||
if kind != skConst and kind != skParam: result = t
|
||||
of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
|
||||
result = nil
|
||||
of tyOrdinal:
|
||||
if kind != skParam: result = t
|
||||
of tyGenericInst, tyDistinct, tyAlias, tyInferred:
|
||||
result = typeAllowedAux(marker, lastSon(t), kind, c, flags)
|
||||
of tyRange:
|
||||
if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin
|
||||
{tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t
|
||||
of tyOpenArray:
|
||||
# you cannot nest openArrays/sinks/etc.
|
||||
if (kind != skParam and views notin c.features) or taIsOpenArray in flags:
|
||||
result = t
|
||||
else:
|
||||
result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
|
||||
of tyVarargs, tySink:
|
||||
# you cannot nest openArrays/sinks/etc.
|
||||
if kind != skParam or taIsOpenArray in flags:
|
||||
result = t
|
||||
else:
|
||||
result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
|
||||
of tyUncheckedArray:
|
||||
if kind != skParam and taHeap notin flags:
|
||||
result = t
|
||||
else:
|
||||
result = typeAllowedAux(marker, lastSon(t), kind, c, flags-{taHeap})
|
||||
of tySequence:
|
||||
if t[0].kind != tyEmpty:
|
||||
result = typeAllowedAux(marker, t[0], kind, c, flags+{taHeap})
|
||||
elif kind in {skVar, skLet}:
|
||||
result = t[0]
|
||||
of tyArray:
|
||||
if t[1].kind == tyTypeDesc:
|
||||
result = t[1]
|
||||
elif t[1].kind != tyEmpty:
|
||||
result = typeAllowedAux(marker, t[1], kind, c, flags)
|
||||
elif kind in {skVar, skLet}:
|
||||
result = t[1]
|
||||
of tyRef:
|
||||
if kind == skConst: result = t
|
||||
else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
|
||||
of tyPtr:
|
||||
result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
|
||||
of tySet:
|
||||
for i in 0..<t.len:
|
||||
result = typeAllowedAux(marker, t[i], kind, c, flags)
|
||||
if result != nil: break
|
||||
of tyObject, tyTuple:
|
||||
if kind in {skProc, skFunc, skConst} and
|
||||
t.kind == tyObject and t[0] != nil:
|
||||
result = t
|
||||
else:
|
||||
let flags = flags+{taField}
|
||||
for i in 0..<t.len:
|
||||
result = typeAllowedAux(marker, t[i], kind, c, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.n != nil:
|
||||
result = typeAllowedNode(marker, t.n, kind, c, flags)
|
||||
of tyEmpty:
|
||||
if kind in {skVar, skLet}: result = t
|
||||
of tyProxy:
|
||||
# for now same as error node; we say it's a valid type as it should
|
||||
# prevent cascading errors:
|
||||
result = nil
|
||||
of tyOwned:
|
||||
if t.len == 1 and t[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
|
||||
result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
|
||||
else:
|
||||
result = t
|
||||
of tyOptDeprecated: doAssert false
|
||||
|
||||
proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
|
||||
# returns 'nil' on success and otherwise the part of the type that is
|
||||
# wrong!
|
||||
var marker = initIntSet()
|
||||
result = typeAllowedAux(marker, t, kind, c, flags)
|
||||
|
||||
proc isViewTypeAux(marker: var IntSet, t: PType): bool
|
||||
|
||||
proc isViewTypeNode(marker: var IntSet, n: PNode): bool =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
result = isViewTypeAux(marker, n.typ)
|
||||
of nkOfBranch:
|
||||
result = isViewTypeNode(marker, n.lastSon)
|
||||
else:
|
||||
for child in n:
|
||||
result = isViewTypeNode(marker, child)
|
||||
if result: break
|
||||
|
||||
proc isViewTypeAux(marker: var IntSet, t: PType): bool =
|
||||
if containsOrIncl(marker, t.id): return false
|
||||
case t.kind
|
||||
of tyVar, tyLent, tyVarargs, tyOpenArray:
|
||||
result = true
|
||||
of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned,
|
||||
tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic, tyFromExpr:
|
||||
result = isViewTypeAux(marker, lastSon(t))
|
||||
of tyTuple:
|
||||
for i in 0..<t.len:
|
||||
result = isViewTypeAux(marker, t[i])
|
||||
if result: break
|
||||
of tyObject:
|
||||
result = false
|
||||
if t.n != nil:
|
||||
result = isViewTypeNode(marker, t.n)
|
||||
if t[0] != nil:
|
||||
result = result or isViewTypeAux(marker, t[0])
|
||||
else:
|
||||
# it doesn't matter what these types contain, 'ptr openArray' is not a
|
||||
# view type!
|
||||
result = false
|
||||
|
||||
proc isViewType*(t: PType): bool =
|
||||
var marker = initIntSet()
|
||||
result = isViewTypeAux(marker, t)
|
||||
Loading…
Add table
Add a link
Reference in a new issue