Skip to content
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

Merged
merged 29 commits into from Jul 19, 2021
Merged

More fixes to cpp_locals #4265

merged 29 commits into from Jul 19, 2021

Conversation

da-woods
Copy link
Contributor

@da-woods da-woods commented Jul 4, 2021

  1. Fix getting an attribute from a function where cpp_locals==False from a class where cpp_locals==True.
  2. Added a 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.
  3. Some of the issues in [BUG] remaining bugs with cpp_locals directive #4266 (iteration, closures, fake reference)

@scoder
Copy link
Contributor

scoder commented Jul 4, 2021

2. Added a `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.

So, we can't just do that test duplication automatically for all C++ specific tests?

@scoder
Copy link
Contributor

scoder commented Jul 4, 2021

  1. Added a 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.

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 no_cpp_locals that disables that duplication if necessary?

@da-woods
Copy link
Contributor Author

da-woods commented Jul 4, 2021

2. Added a `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.

So, we can't just do test duplication automatically that for all C++ specific tests?

The trouble is that the very common:

cdef CppClass c
c.attr()

doesn't work because c is uninitialized (which is correct - it's now behaving like a Python object). Most of the time it'd be possible to change the test, but there's quite a few of them and some may be deliberately testing the old behaviour.

Or, if you want higher test coverage, have a tag no_cpp_locals that disables that duplication if necessary?

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)

Comment on lines 2118 to 2120
self.analyse_rvalue_entry(env)
if entry.is_cpp_optional:
self.dereference_cpp_optional = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Shouldn't analyse_rvalue_entry() do this?
  2. Do we really (really!) need a new and completely option specific field in NameNode (and AttributeNode, 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 …)

Copy link
Contributor Author

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.

Comment on lines 51 to 52
cdef void print_C_destructor "print_C_destructor" () nogil:
print("~C()")
with gil:
Copy link
Contributor

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…)

Copy link
Contributor Author

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

Copy link
Contributor

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.

@da-woods
Copy link
Contributor Author

da-woods commented Jul 4, 2021

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

@da-woods

This comment has been minimized.

Cython/Compiler/Code.py Outdated Show resolved Hide resolved
Cython/Compiler/Code.py Outdated Show resolved Hide resolved
Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
Comment on lines 7387 to 7388
assert undereferenced_result.startswith("(*") and undereferenced_result.endswith(")")
undereferenced_result = undereferenced_result[2:-1]
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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')
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

runtests.py Outdated Show resolved Hide resolved
runtests.py Outdated Show resolved Hide resolved
runtests.py Outdated Show resolved Hide resolved
runtests.py Outdated Show resolved Hide resolved
Cython/Compiler/ParseTreeTransforms.py Outdated Show resolved Hide resolved
Comment on lines 7387 to 7388
assert undereferenced_result.startswith("(*") and undereferenced_result.endswith(")")
undereferenced_result = undereferenced_result[2:-1]
Copy link
Contributor

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.

Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
@@ -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
Copy link
Contributor

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.

Suggested change
# exclude fake references from this because they aren't replaced by optional
# Fake references are not replaced with "std::optional()".

runtests.py Outdated Show resolved Hide resolved
runtests.py Outdated Show resolved Hide resolved
Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
@scoder scoder added this to the 3.0 milestone Jul 19, 2021
@scoder scoder merged commit e1a60bb into cython:master Jul 19, 2021
0dminnimda added a commit to 0dminnimda/cython that referenced this pull request Aug 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants