ProvableHQ / leo

🦁 The Leo Programming Language. A Programming Language for Formally Verified, Zero-Knowledge Applications
https://leo-lang.org/
GNU General Public License v3.0
4.79k stars 655 forks source link

[Bug] Type mismatch when awaiting Future from tuple #28312

Open chirswomack opened 1 month ago

chirswomack commented 1 month ago

🐛 Bug Report

When calling a function that returns a Future as part of a tuple (e.g. transfer_public_to_private) the compiler fails

Steps to Reproduce

Code snippet to reproduce

let transfer_output: (credits.aleo/credits, Future) = credits.aleo/transfer_public_to_private(input_token.owner, amount);
let future: Future = transfer_output.1;

Stack trace & error message

Error [ETYC0372003]: Expected type `Future<Fn()>` but type `Future<Fn()>` was found

Expected Behavior

The compiler should recognize the Future as the correct type

Your Environment

d0cd commented 1 month ago

Adding another example.

import credits.aleo;

program test_credits.aleo {
    async transition send_credits(input: credits.aleo/credits, amount: u64) -> (credits.aleo/credits, Future) {
        let result: (credits.aleo/credits, Future) = credits.aleo/transfer_private_to_public(input, self.address, amount);
        return (result.0, finish(result.1));
    }

    async function finish(f: Future) {
        f.await();
    }
}
chirswomack commented 1 month ago

I'm running into a slightly different issue now. If two different Futures are included in tuples, it duplicates one of them.

Given this program:

import credits.aleo;
import multi_token_support_program.aleo;

program mtsp_credits.aleo {
  async transition deposit_credits_private(
    input_record: credits.aleo/credits,
    amount: u64
  ) -> (credits.aleo/credits, multi_token_support_program.aleo/Token, Future) {
    let transfer_output: (credits.aleo/credits, Future) = credits.aleo/transfer_private_to_public(input_record, self.address, amount);
    let mint_output: (multi_token_support_program.aleo/Token, Future) = multi_token_support_program.aleo/mint_private(
      CREDITS_RESERVED_TOKEN_ID,
      self.address,
      amount as u128,
      false,
      4294967295u32
    );
    return (transfer_output.0, mint_output.0, finalize_deposit_credits_private(transfer_output.1, mint_output.1));
  }

  async function finalize_deposit_credits_private(f0: Future, f1: Future) {
    f0.await();
    f1.await();
  }
}

With the fix on the mainnet branch, the compiler outputs

function deposit_credits_private:
    input r0 as credits.aleo/credits.record;
    input r1 as u64.private;
    call credits.aleo/transfer_private_to_public r0 mtsp_credits.aleo r1 into r2 r3;
    cast r1 into r4 as u128;
    call multi_token_support_program.aleo/mint_private 3443843282313283355522573239085696902919850365217539366784739393210722344986field mtsp_credits.aleo r4 false 4294967295u32 into r5 r6;
    async deposit_credits_private r3 r6 into r7;
    output r2 as credits.aleo/credits.record;
    output r5 as multi_token_support_program.aleo/Token.record;
    output r7 as mtsp_credits.aleo/deposit_credits_private.future;

finalize deposit_credits_private:
    input r0 as multi_token_support_program.aleo/mint_private.future;
    input r1 as multi_token_support_program.aleo/mint_private.future;
    await r0;
    await r1;

where I would expect the finalize to be

finalize deposit_credits_private:
    input r0 as credits.aleo/transfer_private_to_public.future;
    input r1 as multi_token_support_program.aleo/mint_private.future;
    await r0;
    await r1;
akalmykov commented 2 weeks ago

@d0cd I can confirm this problem. We independently tried a similar patch and this has broken type inference exactly as Chris has reported. So at the moment we have to manually patch our compiled code. Are there any ETAs for a fix?

d0cd commented 1 week ago

@akalmykov the team is working on a fix, however, you can get around this issue by destructuring your tuple let (a, b): (credits.aleo/credits, Future) = ...