Repository: vyperlang/vyper

Issue:
`HashMap` of `String` map string's length instead of the string itself
### Version Information

* vyper Version (output of `vyper --version`): 0.3.8+commit.187ab0ee
* OS: OSX
* Python Version (output of `python --version`): 3.8.0

### What's your issue about?

Note that this issue is not present in v0.3.7 but only on the master branch.

When having `String` as the key type of a `HashMap`, the keys are not hashed when accessing an item of the mapping and only the key size is used instead since a simple MLOAD is used.

For example, calling `test` would return `True`
```Vyper
f:HashMap[String[1], bool]
@external
def test() -> bool:
    a:String[1] = "a"
    b:String[1] = "b"
    self.f[a] = True
    return self.f[b] # return True
```

### How can it be fixed?

Replace `BytesT` with `_BytestringT` in https://github.com/vyperlang/vyper/blob/7ff8f3077eec31d3a22ac62c6d7595df4c219dcc/vyper/codegen/expr.py#L337


Discussion:
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/vyperlang/vyper/pull/3384?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) Report
> Merging [#3384](https://app.codecov.io/gh/vyperlang/vyper/pull/3384?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) (984a6d0) into [master](https://app.codecov.io/gh/vyperlang/vyper/commit/7c3cf61665ddc3abb2432f84868b3b6da2be80dd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) (7c3cf61) will **decrease** coverage by `21.52%`.
> The diff coverage is `100.00%`.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang)

```diff
@@             Coverage Diff             @@
##           master    #3384       +/-   ##
===========================================
- Coverage   88.88%   67.37%   -21.52%     
===========================================
  Files          85       85               
  Lines       10706    10706               
  Branches     2233     2233               
===========================================
- Hits         9516     7213     -2303     
- Misses        788     2861     +2073     
- Partials      402      632      +230     
```


| [Impacted Files](https://app.codecov.io/gh/vyperlang/vyper/pull/3384?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang) | Coverage Δ | |
|---|---|---|
| [vyper/codegen/expr.py](https://app.codecov.io/gh/vyperlang/vyper/pull/3384?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang#diff-dnlwZXIvY29kZWdlbi9leHByLnB5) | `62.13% <100.00%> (-25.49%)` | :arrow_down: |

... and [57 files with indirect coverage changes](https://app.codecov.io/gh/vyperlang/vyper/pull/3384/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang)

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=vyperlang)


Patch:
diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py
index 93dce547..7733b333 100644
--- a/vyper/builtins/functions.py
+++ b/vyper/builtins/functions.py
@@ -611,7 +611,7 @@ class Keccak256(BuiltinFunction):
     @process_inputs
     def build_IR(self, expr, args, kwargs, context):
         assert len(args) == 1
-        return keccak256_helper(expr, args[0], context)
+        return keccak256_helper(args[0], context)
 
 
 def _make_sha256_call(inp_start, inp_len, out_start, out_len):
diff --git a/vyper/codegen/events.py b/vyper/codegen/events.py
index 9508a869..30a1b1e5 100644
--- a/vyper/codegen/events.py
+++ b/vyper/codegen/events.py
@@ -15,7 +15,7 @@ def _encode_log_topics(expr, event_id, arg_nodes, context):
             value = unwrap_location(arg)
 
         elif isinstance(arg.typ, _BytestringT):
-            value = keccak256_helper(expr, arg, context=context)
+            value = keccak256_helper(arg, context=context)
         else:
             # TODO block at higher level
             raise TypeMismatch("Event indexes may only be value types", expr)
diff --git a/vyper/codegen/expr.py b/vyper/codegen/expr.py
index dfba4690..5f45f66f 100644
--- a/vyper/codegen/expr.py
+++ b/vyper/codegen/expr.py
@@ -338,11 +338,10 @@ class Expr:
 
         if isinstance(sub.typ, HashMapT):
             # TODO sanity check we are in a self.my_map[i] situation
-            index = Expr.parse_value_expr(self.expr.slice.value, self.context)
-            if isinstance(index.typ, BytesT):
+            index = Expr(self.expr.slice.value, self.context).ir_node
+            if isinstance(index.typ, _BytestringT):
                 # we have to hash the key to get a storage location
-                assert len(index.args) == 1
-                index = keccak256_helper(self.expr.slice.value, index.args[0], self.context)
+                index = keccak256_helper(index, self.context)
 
         elif is_array_like(sub.typ):
             index = Expr.parse_value_expr(self.expr.slice.value, self.context)
@@ -528,8 +527,8 @@ class Expr:
             left = Expr(self.expr.left, self.context).ir_node
             right = Expr(self.expr.right, self.context).ir_node
 
-            left_keccak = keccak256_helper(self.expr, left, self.context)
-            right_keccak = keccak256_helper(self.expr, right, self.context)
+            left_keccak = keccak256_helper(left, self.context)
+            right_keccak = keccak256_helper(right, self.context)
 
             if op not in ("eq", "ne"):
                 return  # raises
diff --git a/vyper/codegen/keccak256_helper.py b/vyper/codegen/keccak256_helper.py
index b2245376..9c5f5eb1 100644
--- a/vyper/codegen/keccak256_helper.py
+++ b/vyper/codegen/keccak256_helper.py
@@ -8,7 +8,7 @@ from vyper.semantics.types.shortcuts import BYTES32_T
 from vyper.utils import SHA3_BASE, SHA3_PER_WORD, MemoryPositions, bytes_to_int, keccak256
 
 
-def _check_byteslike(typ, _expr):
+def _check_byteslike(typ):
     if not isinstance(typ, _BytestringT) and typ != BYTES32_T:
         # NOTE this may be checked at a higher level, but just be safe
         raise CompilerPanic("keccak256 only accepts bytes-like objects")
@@ -18,8 +18,8 @@ def _gas_bound(num_words):
     return SHA3_BASE + num_words * SHA3_PER_WORD
 
 
-def keccak256_helper(expr, to_hash, context):
-    _check_byteslike(to_hash.typ, expr)
+def keccak256_helper(to_hash, context):
+    _check_byteslike(to_hash.typ)
 
     # Can hash literals
     # TODO this is dead code.


Test patch:
diff --git a/tests/parser/features/test_string_map_keys.py b/tests/parser/features/test_string_map_keys.py
new file mode 100644
index 00000000..c52bd728
--- /dev/null
+++ b/tests/parser/features/test_string_map_keys.py
@@ -0,0 +1,25 @@
+def test_string_map_keys(get_contract):
+    code = """
+f:HashMap[String[1], bool]
+@external
+def test() -> bool:
+    a:String[1] = "a"
+    b:String[1] = "b"
+    self.f[a] = True
+    return self.f[b]  # should return False
+    """
+    c = get_contract(code)
+    c.test()
+    assert c.test() is False
+
+
+def test_string_map_keys_literals(get_contract):
+    code = """
+f:HashMap[String[1], bool]
+@external
+def test() -> bool:
+    self.f["a"] = True
+    return self.f["b"]  # should return False
+    """
+    c = get_contract(code)
+    assert c.test() is False