123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- #
- #
- # 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 ast, renderer, options, semdata, types
- import std/intsets
- when defined(nimPreviewSlimSystem):
- import std/assertions
- type
- TTypeAllowedFlag* = enum
- taField,
- taHeap,
- taConcept,
- taIsOpenArray,
- taNoUntyped
- taIsTemplateOrMacro
- taProcContextIsNotMacro
- taIsCastable
- taIsDefaultField
- taVoid # only allow direct void fields of objects/tuples
- 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
- else:
- result = nil
- 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, tySink})
- let flags = if t.kind == tyVoid: flags else: flags-{taVoid}
- case t.kind
- of tyVar, tyLent:
- if kind in {skProc, skFunc, skConst} and (views notin c.features):
- result = t
- elif taIsOpenArray in flags:
- result = t
- elif t.kind == tyLent and ((kind != skResult and views notin c.features) or
- (kind == skParam and {taIsCastable, taField} * flags == {})): # lent cannot be used as parameters.
- # except in the cast environment and as the field of an object
- result = t
- elif isOutParam(t) and kind != skParam:
- result = t
- else:
- var t2 = skipTypes(t.elementType, abstractInst-{tyTypeDesc, tySink})
- 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 and views notin c.features: result = t
- else: result = typeAllowedAux(marker, t2[0], kind, c, flags)
- of tySink:
- result = t
- else:
- if kind notin {skParam, skResult} and views notin c.features: 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 _, a in t.paramTypes:
- if result != nil: break
- result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
- if result.isNil and t.returnType != nil:
- result = typeAllowedAux(marker, t.returnType, 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 tyIterable:
- if kind notin {skParam} or taNoUntyped in flags: result = t
- # tyIterable is only for templates and macros.
- of tyStatic:
- if kind notin {skParam}: result = t
- of tyVoid:
- if taVoid 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.last, 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, skipModifier(t), kind, c, flags)
- of tyRange:
- if skipTypes(t.elementType, abstractInst-{tyTypeDesc}).kind notin
- {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t
- of tyOpenArray:
- # you cannot nest openArrays/sinks/etc.
- if (kind != skParam or taIsOpenArray in flags) and views notin c.features:
- result = t
- else:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
- of tyVarargs:
- # you cannot nest openArrays/sinks/etc.
- if kind != skParam or taIsOpenArray in flags:
- result = t
- else:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
- of tySink:
- # you cannot nest openArrays/sinks/etc.
- if kind != skParam or taIsOpenArray in flags or t.elementType.kind in {tySink, tyLent, tyVar}:
- result = t
- else:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags)
- of tyUncheckedArray:
- if kind != skParam and taHeap notin flags:
- result = t
- else:
- result = typeAllowedAux(marker, elementType(t), kind, c, flags-{taHeap})
- of tySequence:
- if t.elementType.kind != tyEmpty:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
- elif kind in {skVar, skLet}:
- result = t.elementType
- of tyArray:
- if t.elementType.kind == tyTypeDesc:
- result = t.elementType
- elif t.elementType.kind != tyEmpty:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags)
- elif kind in {skVar, skLet}:
- result = t.elementType
- of tyRef:
- if kind == skConst and taIsDefaultField notin flags: result = t
- else: result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
- of tyPtr:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
- of tySet:
- result = typeAllowedAux(marker, t.elementType, kind, c, flags)
- of tyObject:
- if kind in {skProc, skFunc, skConst} and
- t.baseClass != nil and taIsDefaultField notin flags:
- result = t
- else:
- let flags = flags+{taField, taVoid}
- result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
- if result.isNil and t.n != nil:
- result = typeAllowedNode(marker, t.n, kind, c, flags)
- of tyTuple:
- let flags = flags+{taField, taVoid}
- for a in t.kids:
- result = typeAllowedAux(marker, a, 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 tyError:
- # 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.hasElementType and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
- result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
- else:
- result = t
- of tyConcept:
- if kind != skParam: result = t
- else: result = nil
- 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)
- type
- ViewTypeKind* = enum
- noView, immutableView, mutableView
- proc combine(dest: var ViewTypeKind, b: ViewTypeKind) {.inline.} =
- case dest
- of noView, mutableView:
- dest = b
- of immutableView:
- if b == mutableView: dest = b
- proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind
- proc classifyViewTypeNode(marker: var IntSet, n: PNode): ViewTypeKind =
- case n.kind
- of nkSym:
- result = classifyViewTypeAux(marker, n.typ)
- of nkOfBranch:
- result = classifyViewTypeNode(marker, n.lastSon)
- else:
- result = noView
- for child in n:
- result.combine classifyViewTypeNode(marker, child)
- if result == mutableView: break
- proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
- if containsOrIncl(marker, t.id): return noView
- case t.kind
- of tyVar:
- result = mutableView
- of tyLent, tyOpenArray, tyVarargs:
- result = immutableView
- of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned,
- tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
- result = classifyViewTypeAux(marker, skipModifier(t))
- of tyFromExpr:
- if t.hasElementType:
- result = classifyViewTypeAux(marker, skipModifier(t))
- else:
- result = noView
- of tyTuple:
- result = noView
- for a in t.kids:
- result.combine classifyViewTypeAux(marker, a)
- if result == mutableView: break
- of tyObject:
- result = noView
- if t.n != nil:
- result = classifyViewTypeNode(marker, t.n)
- if t.baseClass != nil:
- result.combine classifyViewTypeAux(marker, t.baseClass)
- else:
- # it doesn't matter what these types contain, 'ptr openArray' is not a
- # view type!
- result = noView
- proc classifyViewType*(t: PType): ViewTypeKind =
- var marker = initIntSet()
- result = classifyViewTypeAux(marker, t)
- proc directViewType*(t: PType): ViewTypeKind =
- # does classify 't' without looking recursively into 't'.
- case t.kind
- of tyVar:
- result = mutableView
- of tyLent, tyOpenArray:
- result = immutableView
- of abstractInst-{tyTypeDesc}:
- result = directViewType(t.skipModifier)
- else:
- result = noView
- proc requiresInit*(t: PType): bool =
- (t.flags * {tfRequiresInit, tfNeedsFullInit, tfNotNil} != {}) or
- classifyViewType(t) != noView
|