Repository: vyperlang/vyper

Issue:
Allow interfaces to be used in mappings
### What's your issue about?

Using interfaces as a destination in a mapping:
```python
interface MyInterface: 
    def kick(): modifying

kickers: HashMap[address, MyInterface]
```
raises this exception:
```bash
vyper/types/types.py in parse_type(item, location, sigs, custom_units, custom_structs, constants)
    352             )
    353         else:
--> 354             raise InvalidTypeException("Invalid base type: " + item.id, item)
    355     # Units, e.g. num (1/sec) or contracts
    356     elif isinstance(item, ast.Call) and isinstance(item.func, ast.Name):

InvalidTypeException: line 5:26 Invalid base type: MyInterface
     4
---> 5 kickers: HashMap[address, MyInterface]
---------------------------------^
     6
```

doesn't really seem to be any reason why, especially since it works for standard variables and arrays:
```python
interface MyInterface: 
    def kick(): modifying

kicker: MyInterface
kickers: MyInterface[5]
```

### How can it be fixed?
Allow it to use interfaces as the _destination_ of a mapped entry


Discussion:
This should also include memory and calldata i think.  Currently interfaces can only be assigned as a type within storage.
Yeah, I was thinking about calldata, something like would be neat:
```python
@public
def exchange(token1: ERC20, token2: ERC20):
    ...
```
exactly.  in the signature it's still considered an address, we're just automagically recasting it.  this is how solidity handles it.
Requires a refactor of parser to be implemented, not a breaking change so we can implement in a patch release after v0.2.0

Resolving patch:
diff --git a/vyper/old_codegen/global_context.py b/vyper/old_codegen/global_context.py
--- a/vyper/old_codegen/global_context.py
+++ b/vyper/old_codegen/global_context.py
@@ -120,9 +120,7 @@ def make_struct(self, node: "vy_ast.StructDef") -> list:
                 # A struct must be defined before it is referenced.
                 # This feels like a semantic step and maybe should be pushed
                 # to a later compilation stage.
-                parse_type(
-                    member_type, "storage", custom_structs=self._structs,
-                )
+                self.parse_type( member_type, "storage")
                 members.append((member_name, member_type))
             else:
                 raise StructureException("Structs can only contain variables", item)
@@ -206,7 +204,7 @@ def add_globals_and_events(self, item):
             if isinstance(item.annotation.args[0], vy_ast.Name) and item_name in self._contracts:
                 typ = InterfaceType(item_name)
             else:
-                typ = parse_type(item.annotation.args[0], "storage", custom_structs=self._structs,)
+                typ = self.parse_type(item.annotation.args[0], "storage")
             self._globals[item.target.id] = VariableRecord(
                 item.target.id, len(self._globals), typ, True,
             )
@@ -215,7 +213,7 @@ def add_globals_and_events(self, item):
             self._globals[item.target.id] = VariableRecord(
                 item.target.id,
                 len(self._globals),
-                parse_type(item.annotation, "storage", custom_structs=self._structs,),
+                self.parse_type(item.annotation, "storage"),
                 True,
             )
         else:
diff --git a/vyper/old_codegen/types/types.py b/vyper/old_codegen/types/types.py
--- a/vyper/old_codegen/types/types.py
+++ b/vyper/old_codegen/types/types.py
@@ -181,7 +181,7 @@ def canonicalize_type(t, is_indexed=False):
     raise InvalidType(f"Invalid or unsupported type: {repr(t)}")
 
 
-def make_struct_type(name, location, members, custom_structs):
+def make_struct_type(name, location, sigs, members, custom_structs):
     o = OrderedDict()
 
     for key, value in members:
@@ -189,7 +189,7 @@ def make_struct_type(name, location, members, custom_structs):
             raise InvalidType(
                 f"Invalid member variable for struct {key.id}, expected a name.", key,
             )
-        o[key.id] = parse_type(value, location, custom_structs=custom_structs)
+        o[key.id] = parse_type(value, location, sigs=sigs, custom_structs=custom_structs)
 
     return StructType(o, name)
 
@@ -201,8 +201,10 @@ def parse_type(item, location, sigs=None, custom_structs=None):
     if isinstance(item, vy_ast.Name):
         if item.id in BASE_TYPES:
             return BaseType(item.id)
+        elif (sigs is not None) and item.id in sigs:
+            return InterfaceType(item.id)
         elif (custom_structs is not None) and (item.id in custom_structs):
-            return make_struct_type(item.id, location, custom_structs[item.id], custom_structs,)
+            return make_struct_type(item.id, location, sigs, custom_structs[item.id], custom_structs,)
         else:
             raise InvalidType("Invalid base type: " + item.id, item)
     # Units, e.g. num (1/sec) or contracts
@@ -213,7 +215,7 @@ def parse_type(item, location, sigs=None, custom_structs=None):
                 return InterfaceType(item.args[0].id)
         # Struct types
         if (custom_structs is not None) and (item.func.id in custom_structs):
-            return make_struct_type(item.id, location, custom_structs[item.id], custom_structs,)
+            return make_struct_type(item.id, location, sigs, custom_structs[item.id], custom_structs,)
         raise InvalidType("Units are no longer supported", item)
     # Subscripts
     elif isinstance(item, vy_ast.Subscript):
@@ -233,13 +235,13 @@ def parse_type(item, location, sigs=None, custom_structs=None):
             # List
             else:
                 return ListType(
-                    parse_type(item.value, location, custom_structs=custom_structs,), n_val,
+                    parse_type(item.value, location, sigs, custom_structs=custom_structs,), n_val,
                 )
         elif item.value.id in ("HashMap",) and isinstance(item.slice.value, vy_ast.Tuple):
-            keytype = parse_type(item.slice.value.elements[0], None, custom_structs=custom_structs,)
+            keytype = parse_type(item.slice.value.elements[0], None, sigs, custom_structs=custom_structs,)
             return MappingType(
                 keytype,
-                parse_type(item.slice.value.elements[1], location, custom_structs=custom_structs,),
+                parse_type(item.slice.value.elements[1], location, sigs, custom_structs=custom_structs,),
             )
         # Mappings, e.g. num[address]
         else:


Test patch: