Repository: vyperlang/vyper

Issue:
`flag` Can't Be Imported
### Version Information

* vyper Version (output of `vyper --version`): commit `4b4e188ba83d28b5dd6ff66479e7448e5b925030`
### Issue description

While it is possible to import events and struct types both from `.vy`
and `.vyi` files, it is not possible to import flags.

For interfaces, this is because `InterfaceT` does not hold the flags
defined in the interface and hence `InterfaceT.get_type_member()` only
returns the set of events and struct types defined.

For modules, similarly, flag types are not added to the set of members
of the `ModuleT` at construction time.

Constants not fully supported in `.vyi` files
### What's your issue about?

Given the interface below `foo.vyi` it is currently possible to declare constants:

```vyper
bar: constant(uint256)
```

However, compilation fails when importing the interface in a contract file and trying to use the constant above.

```vyper
import foo

@external
def some_function() -> uint256:
    return foo.bar
```


### How can it be fixed?

Either allow constants to be imported successfully from interfaces (I would prefer this option) or remove the possibility to declare constants in `vyi` files

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) Report
Attention: Patch coverage is `59.09091%` with `9 lines` in your changes missing coverage. Please review.
> Project coverage is 46.63%. Comparing base [(`48a5da4`)](https://app.codecov.io/gh/vyperlang/vyper/commit/48a5da46b7c553d29181880e8ff6d51da62b2679?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) to head [(`2065c23`)](https://app.codecov.io/gh/vyperlang/vyper/commit/2065c238993991e5db8e63e6f734db05b43b80ea?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang).

| [Files with missing lines](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) | Patch % | Lines |
|---|---|---|
| [vyper/semantics/types/module.py](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?src=pr&el=tree&filepath=vyper%2Fsemantics%2Ftypes%2Fmodule.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang#diff-dnlwZXIvc2VtYW50aWNzL3R5cGVzL21vZHVsZS5weQ==) | 55.55% | [7 Missing and 1 partial :warning: ](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) |
| [vyper/semantics/analysis/constant\_folding.py](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?src=pr&el=tree&filepath=vyper%2Fsemantics%2Fanalysis%2Fconstant_folding.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang#diff-dnlwZXIvc2VtYW50aWNzL2FuYWx5c2lzL2NvbnN0YW50X2ZvbGRpbmcucHk=) | 75.00% | [1 Missing :warning: ](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) |

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           master    #4253       +/-   ##
===========================================
- Coverage   91.35%   46.63%   -44.73%     
===========================================
  Files         109      109               
  Lines       15636    15649       +13     
  Branches     3436     3440        +4     
===========================================
- Hits        14285     7298     -6987     
- Misses        921     7716     +6795     
- Partials      430      635      +205     
```

| [Flag](https://app.codecov.io/gh/vyperlang/vyper/pull/4253/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) | Coverage Δ | |
|---|---|---|
| [](https://app.codecov.io/gh/vyperlang/vyper/pull/4253/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) | `46.62% <59.09%> (-44.62%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/vyperlang/vyper/pull/4253?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang).


Patch:
diff --git a/vyper/semantics/types/module.py b/vyper/semantics/types/module.py
index d6cc50a2..dabeaf21 100644
--- a/vyper/semantics/types/module.py
+++ b/vyper/semantics/types/module.py
@@ -21,7 +21,7 @@ from vyper.semantics.data_locations import DataLocation
 from vyper.semantics.types.base import TYPE_T, VyperType, is_type_t
 from vyper.semantics.types.function import ContractFunctionT
 from vyper.semantics.types.primitives import AddressT
-from vyper.semantics.types.user import EventT, StructT, _UserType
+from vyper.semantics.types.user import EventT, FlagT, StructT, _UserType
 from vyper.utils import OrderedSet
 
 if TYPE_CHECKING:
@@ -45,27 +45,29 @@ class InterfaceT(_UserType):
         functions: dict,
         events: dict,
         structs: dict,
+        flags: dict,
     ) -> None:
         validate_unique_method_ids(list(functions.values()))
 
-        members = functions | events | structs
+        members = functions | events | structs | flags
 
         # sanity check: by construction, there should be no duplicates.
-        assert len(members) == len(functions) + len(events) + len(structs)
+        assert len(members) == len(functions) + len(events) + len(structs) + len(flags)
 
         super().__init__(functions)
 
-        self._helper = VyperType(events | structs)
+        self._helper = VyperType(events | structs | flags)
         self._id = _id
         self._helper._id = _id
         self.functions = functions
         self.events = events
         self.structs = structs
+        self.flags = flags
 
         self.decl_node = decl_node
 
     def get_type_member(self, attr, node):
-        # get an event or struct from this interface
+        # get an event, struct or flag from this interface
         return TYPE_T(self._helper.get_member(attr, node))
 
     @property
@@ -159,12 +161,14 @@ class InterfaceT(_UserType):
         interface_name: str,
         decl_node: Optional[vy_ast.VyperNode],
         function_list: list[tuple[str, ContractFunctionT]],
-        event_list: list[tuple[str, EventT]],
-        struct_list: list[tuple[str, StructT]],
+        event_list: Optional[list[tuple[str, EventT]]] = None,
+        struct_list: Optional[list[tuple[str, StructT]]] = None,
+        flag_list: Optional[list[tuple[str, FlagT]]] = None,
     ) -> "InterfaceT":
-        functions = {}
-        events = {}
-        structs = {}
+        functions: dict[str, ContractFunctionT] = {}
+        events: dict[str, EventT] = {}
+        structs: dict[str, StructT] = {}
+        flags: dict[str, FlagT] = {}
 
         seen_items: dict = {}
 
@@ -175,19 +179,20 @@ class InterfaceT(_UserType):
                 raise NamespaceCollision(msg, item.decl_node, prev_decl=prev_decl)
             seen_items[name] = item
 
-        for name, function in function_list:
-            _mark_seen(name, function)
-            functions[name] = function
+        def _process(dst_dict, items):
+            if items is None:
+                return
 
-        for name, event in event_list:
-            _mark_seen(name, event)
-            events[name] = event
+            for name, item in items:
+                _mark_seen(name, item)
+                dst_dict[name] = item
 
-        for name, struct in struct_list:
-            _mark_seen(name, struct)
-            structs[name] = struct
+        _process(functions, function_list)
+        _process(events, event_list)
+        _process(structs, struct_list)
+        _process(flags, flag_list)
 
-        return cls(interface_name, decl_node, functions, events, structs)
+        return cls(interface_name, decl_node, functions, events, structs, flags)
 
     @classmethod
     def from_json_abi(cls, name: str, abi: dict) -> "InterfaceT":
@@ -214,8 +219,7 @@ class InterfaceT(_UserType):
         for item in [i for i in abi if i.get("type") == "event"]:
             events.append((item["name"], EventT.from_abi(item)))
 
-        structs: list = []  # no structs in json ABI (as of yet)
-        return cls._from_lists(name, None, functions, events, structs)
+        return cls._from_lists(name, None, functions, events)
 
     @classmethod
     def from_ModuleT(cls, module_t: "ModuleT") -> "InterfaceT":
@@ -247,8 +251,9 @@ class InterfaceT(_UserType):
         # these are accessible via import, but they do not show up
         # in the ABI json
         structs = [(node.name, node._metadata["struct_type"]) for node in module_t.struct_defs]
+        flags = [(node.name, node._metadata["flag_type"]) for node in module_t.flag_defs]
 
-        return cls._from_lists(module_t._id, module_t.decl_node, funcs, events, structs)
+        return cls._from_lists(module_t._id, module_t.decl_node, funcs, events, structs, flags)
 
     @classmethod
     def from_InterfaceDef(cls, node: vy_ast.InterfaceDef) -> "InterfaceT":
@@ -265,11 +270,7 @@ class InterfaceT(_UserType):
                 )
             functions.append((func_ast.name, ContractFunctionT.from_InterfaceDef(func_ast)))
 
-        # no structs or events in InterfaceDefs
-        events: list = []
-        structs: list = []
-
-        return cls._from_lists(node.name, node, functions, events, structs)
+        return cls._from_lists(node.name, node, functions)
 
 
 # Datatype to store all module information.


Test patch:
diff --git a/tests/functional/codegen/modules/test_interface_imports.py b/tests/functional/codegen/modules/test_interface_imports.py
index c0fae649..3f0f8cb0 100644
--- a/tests/functional/codegen/modules/test_interface_imports.py
+++ b/tests/functional/codegen/modules/test_interface_imports.py
@@ -58,3 +58,31 @@ def foo() -> bool:
     c = get_contract(main, input_bundle=input_bundle)
 
     assert c.foo() is True
+
+
+def test_import_interface_flags(make_input_bundle, get_contract):
+    ifaces = """
+flag Foo:
+    BOO
+    MOO
+    POO
+
+interface IFoo:
+    def foo() -> Foo: nonpayable
+    """
+
+    contract = """
+import ifaces
+
+implements: ifaces
+
+@external
+def foo() -> ifaces.Foo:
+    return ifaces.Foo.POO
+    """
+
+    input_bundle = make_input_bundle({"ifaces.vyi": ifaces})
+
+    c = get_contract(contract, input_bundle=input_bundle)
+
+    assert c.foo() == 4