Solidity Team AMA #2 on Wed, 10th of March 2021

One thing that might not be obvious to everyone is what the difference between public and internal constructors actually was and why visibility was used to express it. For functions the distinction is very clear. External functions are a part of the contract’s ABI and their parameters need to be encoded in a special way. They must be callable from the outside so they’re always included in the dispatch code that checks the selector and determines where to pass control. Internal functions on the other hand have less restrictions on their parameters (they can accept and return types that cannot be ABI-encoded) and they might be entirely skipped by the compiler if it determines that they’re unused.

In case of a constructor this is not the case because it does not even exist as a callable function after the contract is deployed. The only way you could “call” a constructor would be by using the new C() syntax but that does not have the same semantics as a function call. The constructor is only included in the bytecode sent in the data field of the transaction that creates the contract. We call this the creation bytecode. It runs once and its only purpose is to produce another piece of bytecode. This is the deployed (or runtime) bytecode and that’s what actually ends up on the chain. In the simplest case the deployed bytecode is embedded directly in the creation bytecode and just copied. But it can also be modified during construction - this happens for example if your code contains immutable variables whose values are embedded directly in the deployed bytecode.

You could stretch the concept and say that when creating a contract you’re calling its constructor externally and when your constructor calls another one from an inherited contract it’s an internal call. A contract with an external constructor would be one you effectively cannot inherit from while one with an internal one - one you cannot create. Only the latter case is useful in practice which is why at first we made it possible to make the constructor either internal or public. At some point we independently introduced abstract as another restriction you could put on contracts and we thought it was a clearer notion that also covered the same use case and more. This made specifying constructor visibility completely redundant.

1 Like