[Ethereum] Value of msg.sender in call to inherited external function

inheritancesolidity

I found this in the official solidity docs.

The values of all members of msg, including msg.sender and msg.value can change for every external function call. This includes calls to library functions.

The "can change for every external function call" is too vague for me.

Does anyone know when it does change for an external function call?

My concrete example:

pragma solidity ^0.5.10;

contract A {
    function foo() external view returns(address payable) {return msg.sender;}
}

contract B is A {
    address payable public msgSender;

    function bar() external {
        // 3 ways of doing the same thing
        msgSender = A.foo();  // does this work and who will be msgSender?
        msgSender = this.foo();  // does this work and who will be msgSender? 
        msgSender = super.foo();  // does this work and who will be msgSender? 
    }
}

Please take all the code as is => in the 3 notations A.foo() this.foo() super.foo() I am NOT talking to another deployed contract instance. With all 3 ways I want to showcase 3 (different but same) ways of calling external functions (inherited ones in this case) on a contract from inside (internally) that same contract. The 3 ways basically just vary in their scope or explicitness I believe.

Back to my main question:
Who is the msg.sender emitted in the EmitMsgSender event emitted by the call to A/this/super.foo() in my example?
Is it contract B's address payable or is it the address that sent the tx to B.bar()?

By the way, it would make my life easier, if it were the original caller of the B.bar() function, and not B's address.

Thanks for your help!

Best Answer

Here is my answer, to my own questions after testing in Remix:

  1. A.foo() does not work: TypeError Member "foo" not found or not visible after argument-dependent lookup in type(contract A)
  2. same for super.foo() except it says ... lookup in contract super B Both 1) and 2) could be expected from the docs that only list this.f() as a way to call external functions from inside a contract.

And now to the interesting part: Edits thanks to @smarx this.foo()works and it returns the address of contract B as msg.sender.

=> When I call an inherited external function internally (from inside the same contract), the context DID change the msg.sender to the derived contract's address, who forwarded the message to its base contract's external function via the special this.function() syntax.

The msg.sender is NOT preserved (as could be expected from the solidity docs).

However, the docs are still ambiguous:

msg.value can change for every external function call.

This seems to imply that sometimes it does not change. So: When does it change (in my example it did) and most of all when DOES IT NOT?

EDIT: Answer from @smarx (paraphrased)

the context of msg.sender DOES NOT change in calls to an external contract function when you use delegatecall