Repository: vyperlang/vyper

Issue:
`__init__` function can be decorated with any mutability or visibility decorator
### Version Information

* vyper Version (output of `vyper --version`): 0.3.8+commit.046ea166
* OS: OSX
* Python Version (output of `python --version`): 3.8.0

### What's your issue about?

The `__init__` function can be decorated with any mutability or visibility decorator.

- Regarding visibility, compiling a contract with an `internal` constructor will succeed but when trying to deploy the contract, `brownie` and `web3.py` fails with respectively `VirtualMachineError: stack underflow` and `eth.exceptions.InsufficientStack: Wanted 1 stack item as int, had none`.
```Vyper
a:uint256
@internal
def __init__():
    self.a = 1
```

- Regarding mutability, it seems to me that it would be useless to have a `view` or a `pure` constructor as the whole goal of a constructor is to have side effects on the contract's state since it cannot return anything. Note that it is however currently possible to have a constructor assigning immutables with a `view` or a `pure` mutability (the following contract compiles).
```Vyper
a:immutable(uint256)
@external
@pure
def __init__():
    a = 1
```

### How can it be fixed?

Prevent the `__init__` function to have any decorator except for `external`,`payable` and `nonpayable`.



Discussion:

Resolving patch:
diff --git a/vyper/semantics/types/function.py b/vyper/semantics/types/function.py
--- a/vyper/semantics/types/function.py
+++ b/vyper/semantics/types/function.py
@@ -264,11 +264,20 @@ def from_FunctionDef(
         if kwargs["state_mutability"] == StateMutability.PURE and "nonreentrant" in kwargs:
             raise StructureException("Cannot use reentrancy guard on pure functions", node)
 
-        # call arguments
-        if node.args.defaults and node.name == "__init__":
-            raise FunctionDeclarationException(
-                "Constructor may not use default arguments", node.args.defaults[0]
-            )
+        if node.name == "__init__":
+            if (
+                kwargs["state_mutability"] in (StateMutability.PURE, StateMutability.VIEW)
+                or kwargs["function_visibility"] == FunctionVisibility.INTERNAL
+            ):
+                raise FunctionDeclarationException(
+                    "Constructor cannot be marked as `@pure`, `@view` or `@internal`", node
+                )
+
+            # call arguments
+            if node.args.defaults:
+                raise FunctionDeclarationException(
+                    "Constructor may not use default arguments", node.args.defaults[0]
+                )
 
         arguments = OrderedDict()
         max_arg_count = len(node.args.args)


Test patch:
diff --git a/tests/parser/exceptions/test_function_declaration_exception.py b/tests/parser/exceptions/test_function_declaration_exception.py
--- a/tests/parser/exceptions/test_function_declaration_exception.py
+++ b/tests/parser/exceptions/test_function_declaration_exception.py
@@ -48,6 +48,29 @@ def __init__() -> uint256:
 def __init__() -> bool:
     pass
     """,
+    """
+a: immutable(uint256)
+
+@internal
+def __init__():
+    a = 1
+    """,
+    """
+a: immutable(uint256)
+
+@external
+@pure
+def __init__():
+    a = 1
+    """,
+    """
+a: immutable(uint256)
+
+@external
+@view
+def __init__():
+    a = 1
+    """,
 ]