Skip to content

an unconstrained named template parameter is not inferred through a cast and a nested generic call #46

Description

@resetius

an unconstrained named template parameter is not inferred through a cast and a nested generic call

Status

  • Fix agreed upon.
  • Regression case added to externals/qumir/test/regtest/cases/corelang.
  • The generic resolver infers a <named T (template ...)> parameter that
    appears only as a cast target type and as a parameter type of a nested
    generic call, from the concrete type of a sibling template parameter.
  • Full Qumir regtest passes.

Minimal scenario

Two independent named template parameters, Stored and Lookup. Lookup is
the type of a value parameter, so it is always resolved from the call site.
Stored is used only as the target type of a cast from <ptr u8> and as a
parameter type of a nested generic call — never as the type of a value
parameter of the outer function itself.

(block
  (pragma language overloads)

  (fun eq_i64 ((var a i64) (var b i64)) -> bool
    (block (return (== a b))))

  (fun inner
       ((var data <ptr <named Stored (template readable mutable)>>)
        (var key <named Lookup (template readable mutable)>)) -> bool
    (block
      (return (call eq_i64 (index data (: 0 i64)) key))))

  (fun outer
       ((var raw <ptr u8>)
        (var key <named Lookup (template readable mutable)>)) -> bool
    (block
      (var typed = (cast raw <ptr <named Stored (template readable mutable)>>))
      (return (call inner typed key))))

  (fun entry ((var raw <ptr u8>) (var key i64)) -> bool
    (block (return (call outer raw key)))))

entry calls outer with key: i64, so Lookup is unambiguously i64.
Nothing in outer's own parameter list mentions Stored.

Actual behavior

Type annotation leaves Stored as the unresolved placeholder type Named
instead of unifying it with Lookup's concrete type (i64) through the
cast and the nested call. The cast to <ptr <named Stored ...>> produces
a value of type Named, (index data (: 0 i64)) likewise has type Named,
and passing it to eq_i64(i64, i64) fails:

Error: Аргумент #1: нельзя неявно преобразовать тип 'Named' к типу параметра 'Int'.

("Argument #1: cannot implicitly convert type 'Named' to parameter type
'Int'.")

If Stored and Lookup are expected to resolve to two different concrete
types, the failure mode changes but the root cause is the same. Replace
eq_i64/i64 above with two overloads of key_equal(i64, i64) and
(string, string) — and call outer with key: string. Stored is still
left as Named, so resolving key_equal(Stored, Lookup) =
key_equal(Named, string) finds no matching overload:

Error: no matching overload for 'key_equal'

Expected behavior

outer's only generic input is key: Lookup. inner is called with
typed: <ptr Stored> and key: Lookup, and inner's body requires Stored
and Lookup to be compatible with eq_i64/key_equal. The resolver should
propagate Lookup's concrete type through the cast target type and the
nested call to determine Stored, the same way it unifies named template
parameters that appear directly as value-parameter types.

Workaround

Add an extra, otherwise-unused parameter to outer whose declared type is
<named Stored (template readable mutable)>, and pass a value of the
intended concrete type at the call site:

(fun outer
     ((var raw <ptr u8>)
      (var key <named Lookup (template readable mutable)>)
      (var witness <named Stored (template readable mutable)>)) -> bool
  (block
    (var typed = (cast raw <ptr <named Stored (template readable mutable)>>))
    (return (call inner typed key))))

Calling (call outer raw key key) (passing key again as witness) lets
the resolver bind Stored from an ordinary value-parameter type, and both
__generic_outer$Int$Int and __generic_inner$Int$Int (or the $String$String
specializations in the overloaded variant) are produced correctly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions