Files
logos-test-modules/test-fullapi-ext-qtproxy-module/interfaces/full_api_ext.lidl
Dario Gabriel LipicarandClaude Opus 5 f03a5d8bf6 fix(ext): the two providers agree on echoOptional again, at -> ?tstr
One contract had two providers answering different SHAPES.
test_fullapi_ext_rust.lidl declared `echoOptional(v: ?tstr) -> result`;
test_fullapi_ext_cpp is header-first and kept deriving `-> ?tstr` from
`std::optional<std::string> echoOptional(...)`. Carried as known-ext.json's
`ext-optional-return-changed-on-one-provider`, and visible on a second surface
as `ext-optional-return-shape-through-the-qt-proxy`.

THE WORKAROUND OUTLIVED ITS CAUSE. `-> result` was chosen because
`logos-qt-generator --backend consumer` REFUSED `-> ?T` -- null was also how a
failed call reported itself on that path, so an empty ?T and a failure were one
wire value. That gate is retired: `lidlTypeToQt` maps `?T` to
std::optional<T>, so the empty answer is std::nullopt, an inhabitant of its
own, and failure travels on logos::CallError, decided by the C ABI return code
and never by the value's null-ness.

So `-> ?tstr` is emittable today, it is what the C++ provider already derives,
and it is what the case table already expects. Reverting the Rust side is what
makes the two agree; the alternative (moving C++ to `-> result`) would have
meant changing the expectations to match a workaround for a constraint that no
longer exists.

Changed together, because a proxy that forwards a shape must declare the same
one: the Rust LIDL and its impl (`Option<String>` in and out, no success flag
standing in for presence), the qtproxy's own interfaces/full_api_ext.lidl, and
the qtproxy impl's echoOptional -- which now forwards through sOptStr rather
than sResult, and renders with rOpt rather than rResult in the exercise-all and
async blocks.

Both registry entries are retired. The proxy one had a SECOND, independent half
-- a Qt wrapper that could not decode a reply returned a default-constructed
LogosResult, and that default is byte-for-byte a provider refusal -- which is
fixed in logos-qt-sdk#44 and logos-cpp-sdk#150 and stands whichever shape the
contract had chosen. Recorded in the `fixed` entry, because retiring it here
without naming that half would credit the shape change with more than it did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 14:33:47 -03:00

63 lines
2.4 KiB
Plaintext

module full_api_ext {
version "1.0.0"
description "The composite tail of the conformance matrix — records, bytes at depth, typed maps, nested composites and optionality — as a BINDABLE interface. Bound at runtime to test_fullapi_ext_cpp or test_fullapi_ext_rust."
; The three records, spelled exactly as both providers declare them. The Qt
; consumer emits each as a nested struct on the wrapper class
; (FullApiExt::Blob, …), so they do NOT collide with the std-typed structs
; the proxy's own provider surface declares at global scope.
type Blob {
id: tstr
n: uint
payload: bstr
}
type Wrapper {
inner: Blob
tags: [tstr]
blobs: [Blob]
}
; Both optionality spellings are load-bearing here for the same reason the
; providers' contracts give: `? maybe: tstr` (the field flag) and `?tstr` (the
; type kind, in echoOptional) are bound to the same meaning and must emit the
; same code. On the Qt consumer surface that meaning is std::optional<T> —
; std::optional<QString>, std::optional<qulonglong>, std::optional<QByteArray>
; — which is the mapping this interface exists to exercise through a proxy hop.
type Opt {
required: tstr
? maybe: tstr
? count: uint
? blob: bstr
}
method whoAmI() -> tstr
method echoBlob(v: Blob) -> Blob
method echoWrapper(v: Wrapper) -> Wrapper
method echoBlobList(v: [Blob]) -> [Blob]
method echoBlobMap(v: {tstr: Blob}) -> {tstr: Blob}
method echoBytesList(v: [bstr]) -> [bstr]
method echoBytesMap(v: {tstr: bstr}) -> {tstr: bstr}
method echoIntMap(v: {tstr: int}) -> {tstr: int}
method echoStringMap(v: {tstr: tstr}) -> {tstr: tstr}
method echoNestedInts(v: [[int]]) -> [[int]]
method echoMapOfBytesLists(v: {tstr: [bstr]}) -> {tstr: [bstr]}
; `-> result`, following test_fullapi_ext_rust.lidl — the file the matrix
; driver is handed as --contract and the one the ext case table computes its
; coverage from (Optional/scalar/present declares the (result, method_return)
; cell explicitly). test_fullapi_ext_cpp is header-first and still derives
; `-> ?tstr` from std::optional<std::string>; that disagreement is the
; already-registered `ext-optional-return-changed-on-one-provider`, and this
; interface must pick ONE shape rather than invent a third.
method echoOpt(v: Opt) -> Opt
method echoOptList(v: [Opt]) -> [Opt]
method echoOptional(v: ?tstr) -> ?tstr
method fireBlobEvent(v: Blob) -> bool
event blobEvent(v: Blob)
}