1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
|
#
#
# 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
|