Repository: vyperlang/vyper

Issue:
Cannot initialise default values for variable in fn args using `empty`
### Version Information

* vyper Version (output of `vyper --version`): 0.3.4
* OS: osx
* Python Version (output of `python --version`): 3.10.4

### What's your issue about?

Please include information like:

* full output of the error you received
* what command you ran
* the code that caused the failure (see [this link](https://help.github.com/articles/basic-writing-and-formatting-syntax/) for help with formatting code)
* please try running your example with the `--verbose` flag turned on

As of 0.3.4, ZERO_ADDRESS seems to be deprecated in vyper. So, if one tries:

```
A: public(address)

@external
def fill_A():
    self.A = ZERO_ADDRESS
```

You get warned by the compiler:
```
.../vyper/ast/folding.py:153: UserWarning: ZERO_ADDRESS is deprecated. Please use `empty(address)` instead.
  warnings.warn(f"{name} is deprecated. Please use `{replacement}` instead.")
```

The following examples  bork, however:

```
A: public(address)

@external
def fill_A(a: address = empty(address)):
    self.A = a
```

```
A: public(uint256[10])

@external
def fill_A(a: uint256[10] = empty(uint256[10])):
    self.A = a
```

both resulting in:

```
StateAccessViolation: Value must be literal or environment variable
  contract "WillBork", function "fill_A", line 5:24 
       4 @external
  ---> 5 def fill_A(a: address = empty(address)):
  -------------------------------^
       6     self.A = a
```

```
StateAccessViolation: Value must be literal or environment variable
  contract "WillItBork", function "fill_A", line 5:28 
       4 @external
  ---> 5 def fill_A(a: uint256[10] = empty(uint256[10])):
  -----------------------------------^
       6     self.A = a
```

Furthermore, let's introduce a invalid type assignment bug and see if Vyper compiler picks that up. The borky contract is:

```
A: public(uint256[10])

@external
def fill_A(a: address = empty(uint256[10])):
    self.A = a
```

where an `address` is being pre-assigned an `empty(uint256[10])` value as a fn default. On compile, the compiler seems to not care about the type mismatch, and has a bigger issue with `empty(...)`:

```
StateAccessViolation: Value must be literal or environment variable
  contract "WillItBork", function "fill_A", line 5:24 
       4 @external
  ---> 5 def fill_A(a: address = empty(uint256[10])):
  -------------------------------^
       6     self.A = a
```

### How can it be fixed?

Fill this in if you know how to fix it.

The error seems to arise from:

`vyper > semantics > types > functions.py`:

```
namespace = get_namespace()
for arg, value in zip(node.args.args, defaults):
    if arg.arg in ("gas", "value", "skip_contract_check", "default_return_value"):
        raise ArgumentException(
            f"Cannot use '{arg.arg}' as a variable name in a function input", arg
        )
    if arg.arg in arguments:
        raise ArgumentException(f"Function contains multiple inputs named {arg.arg}", arg)
    if arg.arg in namespace:
        raise NamespaceCollision(arg.arg, arg)

    if arg.annotation is None:
        raise ArgumentException(f"Function argument '{arg.arg}' is missing a type", arg)

    type_definition = get_type_from_annotation(
        arg.annotation, location=DataLocation.CALLDATA, is_constant=True
    )
    if value is not None:
       ##################### <---- HERE
        if not check_kwargable(value):
            raise StateAccessViolation(
                "Value must be literal or environment variable", value
            )
        #####################
        validate_expected_type(value, type_definition)

    arguments[arg.arg] = type_definition
```

The `check_kwargable` for `empty(address)` :

![image](https://user-images.githubusercontent.com/11488427/181578145-b63f947c-df23-43d3-8209-b9d254bdd45d.png)


and:

```
In[1]: getattr(value_type, "is_constant", False)
Out[1]: False
```
which then raises a `StateAccessViolation` error and borks everything. So, I guess something needs to be fixed in `vyper > semantics > types > utils.py > check_swargable`















Discussion:

Resolving patch:
diff --git a/vyper/builtin_functions/functions.py b/vyper/builtin_functions/functions.py
--- a/vyper/builtin_functions/functions.py
+++ b/vyper/builtin_functions/functions.py
@@ -2183,6 +2183,10 @@ class Empty(BuiltinFunction):
     # (note that it is ignored in `validate_args`)
     _inputs = [("typename", "TYPE_DEFINITION")]
 
+    # Since `empty` is not folded before semantics validation, this flag is used
+    # for `check_kwargable` in semantics validation.
+    _kwargable = True
+
     def fetch_call_return(self, node):
         type_ = self.infer_arg_types(node)[0].typedef
         return type_
diff --git a/vyper/semantics/types/utils.py b/vyper/semantics/types/utils.py
--- a/vyper/semantics/types/utils.py
+++ b/vyper/semantics/types/utils.py
@@ -226,6 +226,10 @@ def check_constant(node: vy_ast.VyperNode) -> bool:
         if len(args) == 1 and isinstance(args[0], vy_ast.Dict):
             return all(check_constant(v) for v in args[0].values)
 
+        call_type = get_exact_type_from_node(node.func)
+        if getattr(call_type, "_kwargable", False):
+            return True
+
     return False
 
 
@@ -242,6 +246,10 @@ def check_kwargable(node: vy_ast.VyperNode) -> bool:
         if len(args) == 1 and isinstance(args[0], vy_ast.Dict):
             return all(check_kwargable(v) for v in args[0].values)
 
+        call_type = get_exact_type_from_node(node.func)
+        if getattr(call_type, "_kwargable", False):
+            return True
+
     value_type = get_exact_type_from_node(node)
     # is_constant here actually means not_assignable, and is to be renamed
     return getattr(value_type, "is_constant", False)


Test patch:
diff --git a/tests/parser/functions/test_default_parameters.py b/tests/parser/functions/test_default_parameters.py
--- a/tests/parser/functions/test_default_parameters.py
+++ b/tests/parser/functions/test_default_parameters.py
@@ -290,6 +290,20 @@ def foo(bar: Bar = Bar({a: msg.sender, b: 1})): pass
 @external
 def foo(bar: Bar = Bar({a: msg.sender, b: Baz({c: block.coinbase, d: -10})})): pass
     """,
+    """
+A: public(address)
+
+@external
+def foo(a: address = empty(address)):
+    self.A = a
+    """,
+    """
+A: public(int112)
+
+@external
+def foo(a: int112 = min_value(int112)):
+    self.A = a
+    """,
 ]
 
 
diff --git a/tests/parser/syntax/test_constants.py b/tests/parser/syntax/test_constants.py
--- a/tests/parser/syntax/test_constants.py
+++ b/tests/parser/syntax/test_constants.py
@@ -147,6 +147,9 @@ def test() -> int128:
 test_a: constant(uint256) = max_value(uint256)
     """,
     """
+test_a: constant(address) = empty(address)
+    """,
+    """
 TEST_C: constant(uint256) = 1
 TEST_WEI: constant(uint256) = 1