Skip to content

Commit

Permalink
add documentation, support templates
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Aug 15, 2022
1 parent 0c4f3a6 commit 6c75227
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 21 deletions.
4 changes: 2 additions & 2 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,10 @@ proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
typeAllowedCheck(c, typ.n.info, typ, skProc)

proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode
flags: TExprFlags = {}; expectedType: PType = nil): PNode
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}; expectedType: PType = nil): PNode

Expand Down
37 changes: 19 additions & 18 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const
errUndeclaredFieldX = "undeclared field: '$1'"

proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode =
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
rememberExpansion(c, n.info, s)
let info = getCallLineInfo(n)
markUsed(c, info, s)
Expand All @@ -36,7 +36,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
pushInfoContext(c.config, n.info, s.detailedInfo)
result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache,
c.templInstCounter, c.idgen, efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, s, flags, expectedType)
popInfoContext(c.config)

# XXX: A more elaborate line info rewrite might be needed
Expand Down Expand Up @@ -940,15 +941,15 @@ proc setGenericParams(c: PContext, n: PNode) =
for i in 1..<n.len:
n[i].typ = semTypeNode(c, n[i], nil)

proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
return errorNode(c, n)

result = n
let callee = result[0].sym
case callee.kind
of skMacro: result = semMacroExpr(c, result, orig, callee, flags)
of skTemplate: result = semTemplateExpr(c, result, callee, flags)
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
else:
semFinishOperands(c, result)
activate(c, result)
Expand Down Expand Up @@ -989,7 +990,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
let s = bracketedMacro(n[0])
if s != nil:
setGenericParams(c, n[0])
return semDirectOp(c, n, flags)
return semDirectOp(c, n, flags, expectedType)

var t: PType = nil
if n[0].typ != nil:
Expand Down Expand Up @@ -1056,17 +1057,17 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
return result
#result = afterCallActions(c, result, nOrig, flags)
if result[0].kind == nkSym:
result = afterCallActions(c, result, nOrig, flags)
result = afterCallActions(c, result, nOrig, flags, expectedType)
else:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)

proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
# this seems to be a hotspot in the compiler!
let nOrig = n.copyTree
#semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags)
if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
else: result = errorNode(c, n)

proc buildEchoStmt(c: PContext, n: PNode): PNode =
Expand Down Expand Up @@ -2423,9 +2424,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P
var arrayType = newType(tyOpenArray, nextTypeId(c.idgen), expected.owner)
arrayType.rawAddSon(expected[0])
n[1] = semExpr(c, n[1], flags, arrayType)
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
else:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)

proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
# If semCheck is set to false, ``when`` will return the verbatim AST of
Expand Down Expand Up @@ -3006,7 +3007,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
# pretty.checkUse(n[0][1].info, s)
case s.kind
of skMacro, skTemplate:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
of skType:
# XXX think about this more (``set`` procs)
let ambig = c.isAmbiguous
Expand All @@ -3016,7 +3017,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
errorUseQualifier(c, n.info, s)
elif n.len == 1:
result = semObjConstr(c, n, flags, expectedType)
elif s.magic == mNone: result = semDirectOp(c, n, flags)
elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
else: result = semMagic(c, n, s, flags, expectedType)
of skProc, skFunc, skMethod, skConverter, skIterator:
if s.magic == mNone: result = semDirectOp(c, n, flags)
Expand All @@ -3029,17 +3030,17 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
# indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n[0])
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
elif nfDotField in n.flags:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
elif isSymChoice(n[0]):
let b = asBracketExpr(c, n)
if b != nil:
result = semExpr(c, b, flags)
result = semExpr(c, b, flags, expectedType)
else:
result = semDirectOp(c, n, flags)
result = semDirectOp(c, n, flags, expectedType)
else:
result = semIndirectOp(c, n, flags)
result = semIndirectOp(c, n, flags, expectedType)

if nfDefaultRefsParam in result.flags:
result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?)
Expand Down
58 changes: 58 additions & 0 deletions doc/manual_experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,64 @@ ambiguous, a static error will be produced.
p value2
```

Top-down type inference
=======================

In expressions such as:

```nim
let a: T = ex
```

Normally, the compiler type checks the expression `ex` by itself, then
attempts to statically convert the type-checked expression to the given type
`T` as much as it can, while making sure it matches the type. The extent of
this process is limited however due to the expression usually having
an assumed type that might clash with the given type.

With top-down type inference, the expression is type checked with the
extra knowledge that it is supposed to be of type `T`. For example,
the following code is does not compile with the former method, but
compiles with top-down type inference:

```nim
let foo: (float, uint8, cstring) = (1, 2, "abc")
```

The tuple expression has an expected type of `(float, uint8, cstring)`.
Since it is a tuple literal, we can use this information to assume the types
of its elements. The expected types for the expressions `1`, `2` and `"abc"`
are respectively `float`, `uint8`, and `cstring`; and these expressions can be
statically converted to these types.

Without this information, the type of the tuple expression would have been
assumed to be `(int, int, string)`. Thus the type of the tuple expression
would not match the type of the variable, and an error would be given.

The extent of this varies, but there are some notable special cases.

Sequence literals
-----------------

Top-down type inference applies to sequence literals.

```nim
let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]]
```

This behavior is tied to the `@` overloads in the `system` module,
so overloading `@` can disable this behavior. This can be circumvented by
specifying the `` system.`@` `` overload.

```nim
proc `@`(x: string): string = "@" & x
# does not compile:
let x: seq[float] = @[1, 2, 3]
# compiles:
let x: seq[float] = system.`@`([1, 2, 3])
```


Package level objects
=====================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ block:
let x10: array[ABC, byte] = block:
{.gcsafe.}:
[a: 1, b: 2, c: 3]
proc `@`(x: int): int = discard
proc `@`(x: float): float = x + 1
doAssert @1 == 2
let x11: seq[byte] = system.`@`([1, 2, 3])

block:
Expand Down Expand Up @@ -181,3 +182,14 @@ block: # range types

let foo = Foo(x: (1, @{2: @[], 3: @[{'c', 'd'}]}))
doAssert foo.x == (range[1u8..5u8](1u8), @{range[1f32..5f32](2f32): @[], 3f32: @[{range['a'..'e']('c'), 'd'}]})

block: # templates
template foo: untyped = (1, 2, "abc")
let x: (float, byte, cstring) = foo()
doAssert x[0] == float(1)
doAssert x[1] == byte(2)
doAssert x[2] == cstring("abc")
let (a, b, c) = x
doAssert a == float(1)
doAssert b == byte(2)
doAssert c == cstring("abc")

0 comments on commit 6c75227

Please sign in to comment.