diff --git a/compiler/sem/sem.nim b/compiler/sem/sem.nim index 4a3d75c9cf8..dae720efd1f 100644 --- a/compiler/sem/sem.nim +++ b/compiler/sem/sem.nim @@ -914,6 +914,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; c.semTypeNode = semTypeNode c.instTypeBoundOp = sigmatch.instTypeBoundOp c.hasUnresolvedArgs = hasUnresolvedArgs + c.semGenericExpr = semGenericExpr c.templInstCounter = new int pushProcCon(c, module) diff --git a/compiler/sem/semdata.nim b/compiler/sem/semdata.nim index 184edbe0536..5a08d14e770 100644 --- a/compiler/sem/semdata.nim +++ b/compiler/sem/semdata.nim @@ -637,6 +637,9 @@ type op: TTypeAttachedOp; col: int): PSym {.nimcall.} ## read to break cyclic dependencies, init in sem during module open and ## read in liftdestructors and semtypinst + semGenericExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} + ## read to break cyclic dependencies, init in sem during module open and + ## read in sigmatch # ------------------------------------------------------------------------- # end: not entirely clear why, function pionters for certain sem calls? # ------------------------------------------------------------------------- diff --git a/compiler/sem/semtypes.nim b/compiler/sem/semtypes.nim index ac82a2dc5b5..22408dffdf2 100644 --- a/compiler/sem/semtypes.nim +++ b/compiler/sem/semtypes.nim @@ -1696,6 +1696,13 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = else: result = nil +proc semGenericExpr(c: PContext, n: PNode): PNode = + ## Runs the generic pre-pass on `n` and returns the result. Similar to + ## ``semGenericStmt``, but makes sure that all generic parameter symbols + ## were bound. + result = semGenericStmt(c, n) + discard fixupTypeVars(c, result) + proc semGenericParamInInvocation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) n.typ = makeTypeDesc(c, result) diff --git a/compiler/sem/sigmatch.nim b/compiler/sem/sigmatch.nim index 0669d5c492b..faf78bb9d6d 100644 --- a/compiler/sem/sigmatch.nim +++ b/compiler/sem/sigmatch.nim @@ -3120,6 +3120,127 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int m.error.firstMismatch.pos = a m.error.firstMismatch.formal = formal +proc matchesType(c: PContext, n: PNode, m: var TCandidate, + marker: var IntSet) = + ## Matches the arguments taken from invocation expression `n` against the + ## ``tyGenericBody`` callee and fills `m` with the results. `marker` is + ## updated with the matched-against formal positions. + m.state = csMatch # until proven otherwise + m.error.firstMismatch = MismatchInfo() + + # pre-pass: make sure the AST is valid. `n` is production AST, so it can be + # modified in-place + var hasError = false + for i in 1.. type mismatch + m.call[formal.position + 1] = copyNodeWithKids(operand) + m.call[formal.position + 1].typ = makeTypeFromExpr(c, operand) + break + else: + operand = m.c.semOperand(m.c, operand) + arg = paramTypesMatch(m, formal.typ, operand.typ, operand) + + if arg != nil: + # errors don't need to be considered here; they're handled through + # `fauxMatch` + m.call[formal.position + 1] = arg + else: + # legacy error handling + m.call[formal.position + 1] = operand + break + + f = max(formal.position + 1, f + 1) + inc i + + if i < n.len: + # an error occurred + m.state = csNoMatch + m.error.firstMismatch.pos = i + m.error.firstMismatch.arg = n[i] + m.error.firstMismatch.formal = formal + proc semFinishOperands*(c: PContext, n: PNode) = # this needs to be called to ensure that after overloading resolution every # argument has been sem'checked: @@ -3155,7 +3276,10 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = return var marker = initIntSet() - matchesAux(c, n, nOrig, m, marker) + if m.callee.kind == tyGenericBody: + matchesType(c, n, m, marker) + else: + matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return diff --git a/tests/lang_callable/generics/tdependent_operands_to_static.nim b/tests/lang_callable/generics/tdependent_operands_to_static.nim new file mode 100644 index 00000000000..0c8de08e2b6 --- /dev/null +++ b/tests/lang_callable/generics/tdependent_operands_to_static.nim @@ -0,0 +1,27 @@ +discard """ + description: ''' + Various tests for operands to `static` constrained generic type parameters + where the operand's type depends on unresolved type variables. + ''' +""" + +type + Type1[T: static] = object ## only must be *some* static value + Type2[T: static int] = object ## must a be a static int + +proc eval[T](x: T): T {.compileTime.} = + x + +proc p1[T](): Type1[eval(default(T))] = discard +proc p2[T](): Type2[eval(default(T))] = discard +# ^^ whether the ``Type2`` can be instantiated depends on the later +# supplied `T` + +discard p1[int]() # works +discard p1[float]() # works +discard p1[string]() # works + +discard p2[int]() # int is convertible to int -> works +discard p2[float]() # float is convertible to int -> works +# string is not convertible to float -> fails: +doAssert not compiles(p3[string]()) diff --git a/tests/lang_callable/generics/tdependent_operands_to_static_2.nim b/tests/lang_callable/generics/tdependent_operands_to_static_2.nim new file mode 100644 index 00000000000..9ef469e2061 --- /dev/null +++ b/tests/lang_callable/generics/tdependent_operands_to_static_2.nim @@ -0,0 +1,21 @@ +discard """ + description: ''' + Operands with a type not known upfront cannot be used as arguments to + complex static + ''' + errormsg: "cannot instantiate Type" + line: 21 + knownIssue: ''' + `Type`s generic parameter is not detected as containing a `static`, thus + full analysis is not disabled (which subsequently fails) + ''' +""" + +type + Type[T: string | static float] = object ## must be a string or static float + +proc eval[T](x: T): T {.compileTime.} = x + +# the operand's type is not known and no concrete type can be derived from +# the constraint -> reject early +proc p[T](): Type[eval(default(T))] = discard