From 894a19c6ed20ecfc72ea4bda010c11371b571d45 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 13 Jun 2023 21:05:44 +0300 Subject: fix calls in generic bodies, delay typecheck when no overloads match (#22029) * sacrifice "tgenericshardcases" for working statics * legacy switch for CI, maybe experimental later * convert to experimental * apparently untyped needs the experimental switch * try special case call semcheck * try fix * fix compilation * final cleanup, not experimental, make `when` work * remove last needed use of untyped * fix unused warning in test * remove untyped feature --- compiler/semcall.nim | 5 ++++- compiler/semexprs.nim | 5 ++++- compiler/seminst.nim | 3 +-- compiler/semtypes.nim | 19 +++++++++++++++---- compiler/semtypinst.nim | 46 ++++++++++++++++++++++++---------------------- compiler/sigmatch.nim | 8 +++++++- compiler/types.nim | 5 ++++- 7 files changed, 59 insertions(+), 32 deletions(-) (limited to 'compiler') diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8445b25e7..c0317f4d6 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -627,7 +627,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, candidates) result = semResolvedCall(c, r, n, flags) else: - if efExplain notin flags: + if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: + result = n + result.typ = makeTypeFromExpr(c, result.copyTree) + elif efExplain notin flags: # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 53c0790fe..3b40425cf 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -971,7 +971,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, if result != nil: if result[0].kind != nkSym: - internalError(c.config, "semOverloadedCallAnalyseEffects") + if not (efDetermineType in flags and c.inGenericContext > 0): + internalError(c.config, "semOverloadedCallAnalyseEffects") return let callee = result[0].sym case callee.kind @@ -1007,6 +1008,8 @@ proc setGenericParams(c: PContext, n: 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) + if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0: + return n result = n diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 3afbedec7..b4b5ad0a2 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -388,8 +388,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): n[bodyPos] = copyTree(getBody(c.graph, fn)) - if c.inGenericContext == 0: - instantiateBody(c, n, fn.typ.n, result, fn) + instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 14ad72fca..a9fc980db 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -269,6 +269,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, "range is empty") var range: array[2, PNode] + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types range[0] = semExprWithType(c, n[1], {efDetermineType}) range[1] = semExprWithType(c, n[2], {efDetermineType}) @@ -277,7 +279,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen) let hasUnknownTypes = c.inGenericContext > 0 and - rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr + (rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr) if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): @@ -337,6 +339,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType = elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<": result = errorType(c) else: + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types let e = semExprWithType(c, n, {efDetermineType}) if e.typ.kind == tyFromExpr: result = makeRangeWithStaticExpr(c, e.typ.n) @@ -357,7 +361,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) # This is an int returning call, depending on an - # yet unknown generic param (see tgenericshardcases). + # yet unknown generic param (see tuninstantiatedgenericcalls). # We are going to construct a range type that will be # properly filled-out in semtypinst (see how tyStaticExpr # is handled there). @@ -380,7 +384,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = let indx = semArrayIndex(c, n[1]) var indxB = indx if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB) - if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}: + if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and + tfUnresolved notin indxB.flags: if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}: discard elif not isOrdinalType(indxB): @@ -737,7 +742,13 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, if e.kind != nkIntLit: discard "don't report followup error" elif e.intVal != 0 and branch == nil: branch = it[1] else: - it[0] = forceBool(c, semExprWithType(c, it[0])) + # XXX this is still a hard compilation in a generic context, this can + # result in unresolved generic parameters being treated like real types + let e = semExprWithType(c, it[0], {efDetermineType}) + if e.typ.kind == tyFromExpr: + it[0] = makeStaticExpr(c, e) + else: + it[0] = forceBool(c, e) of nkElse: checkSonsLen(it, 1, c.config) if branch == nil: branch = it[0] diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 50dba515d..7c9bf9039 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -141,27 +141,28 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skGenericParam or (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) -proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = - # This is needed for tgenericshardcases - # It's possible that a generic param will be used in a proc call to a - # typedesc accepting proc. After generic param substitution, such procs - # should be optionally instantiated with the correct type. In order to - # perform this instantiation, we need to re-run the generateInstance path - # in the compiler, but it's quite complicated to do so at the moment so we - # resort to a mild hack; the head symbol of the call is temporary reset and - # overload resolution is executed again (which may trigger generateInstance). - if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags: - var needsFixing = false - for i in 1.. 0 and + a.skipTypes({tyTypeDesc}).kind == tyGenericParam: + # generic type bodies can sometimes compile call expressions + # prevent unresolved generic parameters from being passed to procs as + # typedesc parameters + result = isNone + elif a.kind != tyTypeDesc: if a.kind == tyGenericParam and tfWildcard in a.flags: # TODO: prevent `a` from matching as a wildcard again result = isGeneric diff --git a/compiler/types.nim b/compiler/types.nim index 353c445a6..4a7514370 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -209,6 +209,8 @@ proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter, # a leaf result = iterOverTypeAux(marker, n.typ, iter, closure) else: + result = iterOverTypeAux(marker, n.typ, iter, closure) + if result: return for i in 0..