New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More fixes to cpp_locals #4265
More fixes to cpp_locals #4265
Conversation
To mark a test as suitable for running with cpp_locals
So, we can't just do that test duplication automatically for all C++ specific tests? |
Or, if you want higher test coverage, have a tag |
The trouble is that the very common:
doesn't work because
Yeah - opt-out could possibly be better than opt-in. It at least ensures that new tests are written to work in both (unless there's good reason) |
Cython/Compiler/ExprNodes.py
Outdated
self.analyse_rvalue_entry(env) | ||
if entry.is_cpp_optional: | ||
self.dereference_cpp_optional = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Shouldn't
analyse_rvalue_entry()
do this? - Do we really (really!) need a new and completely option specific field in
NameNode
(andAttributeNode
, as it seems) for this? Or is it information that we can infer from some existing state? (It's an rvalue, it's cpp-optional, so …)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or is it information that we can infer from some existing state? (It's an rvalue, it's cpp-optional, so …)
In principle the .is_target
attribute should tell us that it's a rvalue. In practice that doesn't look to be set (ever). I've changed it so that .is_target
is actually set and intered it as you suggest.
tests/run/cpp_optional_temps.pyx
Outdated
cdef void print_C_destructor "print_C_destructor" () nogil: | ||
print("~C()") | ||
with gil: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this use the with gil
function declaration? (And I also wonder how this ever worked…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I also wonder how this ever worked…
I have a vague feeling that some simple print statements are able to work without the GIL (by design?). But it for some reason it stopped working, and it definitely isn't the point of the test so I didn't investigate further
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a vague feeling that some simple print statements are able to work without the GIL (by design?)
Ah, sure. You switched the test from Py2 to Py3, which turned the print
statement into a print()
function call. Print statements implicitly acquire the GIL, for convenience. Print function calls are not so easy to spot. See the InjectGILHandling
transform.
I've made the tests opt-in. I'm expecting to be caught out by a few in the compile directory because I haven't looked at any of those yet |
This comment has been minimized.
This comment has been minimized.
Cython/Compiler/ExprNodes.py
Outdated
assert undereferenced_result.startswith("(*") and undereferenced_result.endswith(")") | ||
undereferenced_result = undereferenced_result[2:-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I don't know how to do this better right now, but it's probably not something that you're proud of either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No this isn't great.
Another option might be to add an optional argument to AttributeNote.result
to get the result with/without dereferencing. Breaks the general interface though, but my making it an optional argument other callers shouldn't notice. I'm not sure which is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, it feels more like calling .result()
isn't the right thing and there should be some intermediate method. The result()
method is clearly doing too much here. Adding an option to it would rather make it worse.
I could imagine splitting most of calculate_result_code()
out into a new method calculate_access_code()
, and then calling that from calculate_result_code()
and from here.
runtests.py
Outdated
if (self.add_cpp_locals_extra_tests and 'cpp' in languages and | ||
'cpp' in tags['tag'] and not 'no-cpp-locals' in tags['tag']): | ||
languages = list(languages) | ||
languages.append('cpp_locals') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. A 'language' doesn't really seem the right way to express this. We already split tests for .py
files. Are the requirements for this tag really different?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's not quite a language. The nice thing about misusing language is that it creates them in a separate cpp_locals directory and reports the tests as cpp_locals/testnames without too much extra effort.
The approach we use for the .py files doesn't quite work - that creates a separate PureDoctestTestCase
at a much earlier stage before the CythonRunTestCase
is created (which makes sense there because it's quite a different test, but doesn't make so much sense here where it's largely the same test but with different options).
What would work is changing:
tests = [ self.build_test(test_class, path, workdir, module, module_path,
tags, language, language_level,
expect_errors, expect_warnings, warning_errors, preparse,
pythran_dir if language == "cpp" else None,
add_cython_import=add_cython_import)
for language in languages
for preparse in preparse_list
for language_level in language_levels
]
to
tests = [ self.build_test(test_class, path, workdir, module, module_path,
tags, language, language_level,
expect_errors, expect_warnings, warning_errors, preparse,
pythran_dir if language == "cpp" else None,
add_cython_import=add_cython_import,
cpp_locals_on=cpp_locals_on)
for language in languages
for preparse in preparse_list
for language_level in language_levels
for cpp_locals_on in cpp_locals_states
]
(plus a few supporting changes elsewhere). To me that feels more intrusive that using 'language', but I'm happy to make that change if you disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty much what needs to be done, I think, but we should use a list of extra_directives
dicts. Given that we will eventually need to test at least some directives separately or in combination, I think this one isn't special enough to merit its own option.
You can keep the no_cpp_locals
tag by simply checking if the part without the no_
is in the extra directives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah a generic solution is probably better so I've done that. In principle it might be possible to handle language level in the extra directives too but I decided it was safer not to touch that for now.
since they're assigned to elsewhere now
…locals_more_fixes
Co-authored-by: scoder <stefan_ml@behnel.de>
Cython/Compiler/ExprNodes.py
Outdated
assert undereferenced_result.startswith("(*") and undereferenced_result.endswith(")") | ||
undereferenced_result = undereferenced_result[2:-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, it feels more like calling .result()
isn't the right thing and there should be some intermediate method. The result()
method is clearly doing too much here. Adding an option to it would rather make it worse.
I could imagine splitting most of calculate_result_code()
out into a new method calculate_access_code()
, and then calling that from calculate_result_code()
and from here.
Co-authored-by: scoder <stefan_ml@behnel.de>
@@ -3166,7 +3166,9 @@ def visit_ModuleNode(self, node): | |||
def visit_ExprNode(self, node): | |||
self.visitchildren(node) | |||
if (self.current_env().directives['cpp_locals'] and | |||
node.is_temp and node.type.is_cpp_class): | |||
node.is_temp and node.type.is_cpp_class and | |||
# exclude fake references from this because they aren't replaced by optional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this comment adds anything that's not obvious from the line that follows. An explanation of the reason would help more.
# exclude fake references from this because they aren't replaced by optional | |
# Fake references are not replaced with "std::optional()". |
This reverts commit e1a60bb.
cpp_locals==False
from a class wherecpp_locals==True
.cpp_locals
tag to the test-suite that causes a file to get rerun with the cpp_locals directive on, and set it in suitable files. This should improve the test coverage for the feature. (Most of the files where it isn't set just have "local variable referenced before assignment" errors, which is expected and correct). There are a couple of real bugs that it's exposed though.cpp_locals
directive #4266 (iteration, closures, fake reference)