Repository: cython/cython

Issue:
[BUG] Calling 'operator bool' results in 'AttributeError'
<!--
**PLEASE READ THIS FIRST:**
- Do not use the bug and feature tracker for support requests. Use the `cython-users` mailing list instead.
- Did you search for similar issues already? Please do, it helps to save us precious time that we otherwise could not invest into development.
- Did you try the latest master branch or pre-release? It might already have what you want to report. Also see the [Changelog](https://github.com/cython/cython/blob/master/CHANGES.rst) regarding recent changes.
-->

**Describe the bug**
Declaring a C++ string and then invoking an `if <identifier>:` operation on it raises an `AttributeError`:

```
[1/1] Cythonizing test.pyx
Traceback (most recent call last):
  File "/Users/lkisskollar/tmp/cython_bug/setup.py", line 4, in <module>
    setup(name="test", version="0.1.0", ext_modules=cythonize(["test.pyx"], language_level=3))
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Build/Dependencies.py", line 1116, in cythonize
    cythonize_one(*args)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Build/Dependencies.py", line 1262, in cythonize_one
    result = compile_single(pyx_file, options, full_module_name=full_module_name)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Main.py", line 577, in compile_single
    return run_pipeline(source, options, full_module_name)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Main.py", line 505, in run_pipeline
    err, enddata = Pipeline.run_pipeline(pipeline, source)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Pipeline.py", line 372, in run_pipeline
    data = run(phase, data)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Pipeline.py", line 352, in run
    return phase(data)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Pipeline.py", line 52, in generate_pyx_code_stage
    module_node.process_implementation(options, result)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/ModuleNode.py", line 206, in process_implementation
    self.generate_c_code(env, options, result)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/ModuleNode.py", line 501, in generate_c_code
    self.body.generate_function_definitions(env, code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 403, in generate_function_definitions
    stat.generate_function_definitions(env, code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 2067, in generate_function_definitions
    self.generate_function_body(env, code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 1827, in generate_function_body
    self.body.generate_execution_code(code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 409, in generate_execution_code
    stat.generate_execution_code(code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 6798, in generate_execution_code
    if_clause.generate_execution_code(code, end_label, is_last=i == last)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Nodes.py", line 6837, in generate_execution_code
    self.condition.generate_evaluation_code(code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/ExprNodes.py", line 6210, in generate_evaluation_code
    self.function.generate_evaluation_code(code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/ExprNodes.py", line 829, in generate_evaluation_code
    self.generate_result_code(code)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/ExprNodes.py", line 7360, in generate_result_code
    code.intern_identifier(self.attribute),
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Code.py", line 1948, in intern_identifier
    return self.get_py_string_const(text, identifier=True)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Code.py", line 1938, in get_py_string_const
    return self.globalstate.get_py_string_const(
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Code.py", line 1379, in get_py_string_const
    c_string = self.get_string_const(text)
  File "/Users/lkisskollar/tmp/cython_bug/venv/lib/python3.9/site-packages/Cython/Compiler/Code.py", line 1350, in get_string_const
    if text.is_unicode:
AttributeError: 'str' object has no attribute 'is_unicode'
```

**To Reproduce**
Code to reproduce the behaviour:
```cython
# distutils: language=c++

from libcpp.string cimport string

cdef foo():
    cdef string field
    if field:
        pass
```

**Expected behavior**
No compiler crash and an error message that this is an invalid operation.

**Environment (please complete the following information):**
 - OS: [e.g. Linux, Windows, macOS] macOS
 - Python version [e.g. 3.8.4] 3.9.4
 - Cython version [e.g. 0.29.18] 0.29.24 and 3.0.0a9



Discussion:

Resolving patch:
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -1012,11 +1012,11 @@ def coerce_to_boolean(self, env):
             return self
         elif type.is_pyobject or type.is_int or type.is_ptr or type.is_float:
             return CoerceToBooleanNode(self, env)
-        elif type.is_cpp_class:
+        elif type.is_cpp_class and type.scope and type.scope.lookup("operator bool"):
             return SimpleCallNode(
                 self.pos,
                 function=AttributeNode(
-                    self.pos, obj=self, attribute='operator bool'),
+                    self.pos, obj=self, attribute=StringEncoding.EncodedString('operator bool')),
                 args=[]).analyse_types(env)
         elif type.is_ctuple:
             bool_value = len(type.components) == 0


Test patch:
diff --git a/tests/errors/cpp_bool.pyx b/tests/errors/cpp_bool.pyx
new file mode 100644
--- /dev/null
+++ b/tests/errors/cpp_bool.pyx
@@ -0,0 +1,13 @@
+# tag: cpp
+# mode: error
+
+from libcpp.string cimport string
+
+cdef foo():
+    cdef string field
+    if field:  # field cannot be coerced to book
+        pass
+
+_ERRORS = u"""
+8:7: Type 'string' not acceptable as a boolean
+"""