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

Add facility to support with-contexts #3017

Merged
merged 27 commits into from Aug 14, 2018

Conversation

sklam
Copy link
Member

@sklam sklam commented Jun 6, 2018

Adds pluggable facility to support with contexts. Two builtin contextmanagers are implemented for testing.

@codecov-io
Copy link

codecov-io commented Jun 6, 2018

Codecov Report

Merging #3017 into master will increase coverage by 2.39%.
The diff coverage is 81%.

@@            Coverage Diff            @@
##           master   #3017      +/-   ##
=========================================
+ Coverage    78.7%   81.1%   +2.39%     
=========================================
  Files         384     386       +2     
  Lines       75091   75675     +584     
  Branches     8436    8488      +52     
=========================================
+ Hits        59098   61373    +2275     
+ Misses      14740   13003    -1737     
- Partials     1253    1299      +46

Copy link
Contributor

@stuartarchibald stuartarchibald left a comment

Choose a reason for hiding this comment

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

Thanks for the patch @sklam. Looks like a good start on this feature set. Only a few minor comments, I'll add more items directly to the ticket.

def get_call_template(self, args, kws):
"""
Get a typing.ConcreteTemplate for this dispatcher and the given
*args* and *kws* types. This allows to resolve the return type.
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps This enables the resolving of the return type.?


def fill_callee_prologue(block, inputs, label_next):
"""
Fill a new block *block* that unwraps argument using names in *inputs* and
Copy link
Contributor

Choose a reason for hiding this comment

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

s/argument/arguments/

def fill_callee_prologue(block, inputs, label_next):
"""
Fill a new block *block* that unwraps argument using names in *inputs* and
then jump to *label_next*.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/jump/jumps/

return block
entry_block = blocks[loopinfo.callfrom]
scope = entry_block.scope
loc = entry_block.loc

def make_epilogue():
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this function now dead? Think ir_utils.fill_callee_epilogue does the functional equivalent?



def _get_with_contextmanager(func_ir, blocks, blk_start):
"""Get the global object used for as the context manager
Copy link
Contributor

Choose a reason for hiding this comment

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

s/as//

counters.pop(ir.Del, None)
# There MUST NOT be any other statements
if counters:
raise errors.CompilerError(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can a ref to loc be easily supplied here such that the location will be pointed at by the error handlers?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added loc

)


def _cfg_nodes_in_region(cfg, region_begin, region_end):
Copy link
Contributor

Choose a reason for hiding this comment

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

Wonder whether this should perhaps go in ir_utils or controlflow ?

Copy link
Member Author

Choose a reason for hiding this comment

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

There are a number of things in transforms.py that can go into other modules. I want to defer that task to later.

for s, e in withs:
if s not in doms[e]:
msg = "Entry of with-context not dominating the exit."
raise errors.CompilerError(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can a loc be supplied to these two errors?


def _bypass_with_context(blocks, blk_start, blk_end, forwardvars):
"""Given the starting and ending block of the with-context,
replaces a new head block that jumps to the end.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be replaces the head block with a new block that jumps to the end.?



class _CallContextType(WithContext):
"""A simple context-manager that tells the compiler lift the body of the
Copy link
Contributor

Choose a reason for hiding this comment

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

s/compiler/compiler to/

@stuartarchibald
Copy link
Contributor

Just done a bit of manual testing... some items for discussion

  1. Seems like ir.EnterWith is not understood in object mode, or @vectorize, or @stencil and I imagine also @guvectorize.
  2. Assuming it is desirable to fix 1. (which it may not be, in which case a more graceful error should be produced), the somewhat bizarre situation arises where object mode execution is potentially lifting out with-blocks as functions that are also running in object mode (cf. Loop lifting has same issue). I think this sort of thing might trigger it:
def objmode_with_objmode():
  object()
  with call_context:
    object()
  return 1
  1. Parfors is very broken. e.g.
def lift():
  with call_context:
    pass
print(njit(parallel=True)(lift)())

gives:

  File "<path>/numba/numba/stencilparfor.py", line 49, in run
    if isinstance(call_list[0], StencilFunc):
IndexError: Failed at nopython (nopython frontend)
Failed at nopython (convert to parfors)
list index out of range

which was somewhat unexpected as there's no stencil anywhere near the source function!

Finally, is this to be documented in the developer documentation, or should that wait until more
features are added and the API etc stabilizes? Also, does this impact annotations, I don't think it will but am mindful that not breaking #2793 would be good!

@sklam
Copy link
Member Author

sklam commented Jun 12, 2018

#3017 (comment) raised important concerns. Given how this patch is not adding any user facing feature yet, I am ok to delay the merge until some of the concerns are resolved even if it will not make it this release cycle. Particularly, the breaking of parfor features should be addressed first.

As for the objectmode support, there were no support for contextmanager in objectmode before. It will fail in the bytecode analysis. I think it is acceptable to skip objectmode support for this PR and just do a better errmsg for it. Future patches should implement the objectmode support.

@ehsantn
Copy link
Collaborator

ehsantn commented Jun 18, 2018

@sklam the Parfor-related issue was a simple bug that I just fixed.

@stuartarchibald stuartarchibald added this to the Numba 0.40 RC milestone Jun 20, 2018
# Conflicts:
#	numba/ir.py
#	numba/ir_utils.py
@sklam sklam mentioned this pull request Jul 25, 2018
2 tasks
@sklam
Copy link
Member Author

sklam commented Jul 26, 2018

Oh, looks like I need to resolve the loc review comments first.

@sklam
Copy link
Member Author

sklam commented Jul 26, 2018

This is ready for review now

contextmanager : IR value
begin, end : int
The beginning and the ending offset of the with-body.
loc : int
Copy link
Contributor

Choose a reason for hiding this comment

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

loc is a ir.Loc instance?

njit(liftcall4)()
# Known error. We only support one context manager per function
# for body that are lifted.
self.assertIn("re-entrant", str(raises.exception))
Copy link
Contributor

Choose a reason for hiding this comment

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

Any view on doing something with non-existent context managers? I just accidentally forgot to import the call_context, but this equally applies to e.g.

from numba import njit
def bar():
    with unknown_ctx:
        pass
njit(bar)()

which gives the same fairly spectacular traceback (as the one if you forget the import) ending with:

<path>/numba/numba/transforms.py in with_lifting(func_ir, typingctx, targetctx, flags, locals)
    293         _legalize_with_head(blocks[blk_start])
    294         cmkind = _get_with_contextmanager(func_ir, blocks, blk_start)
--> 295         sub = cmkind.mutate_with_body(func_ir, blocks, blk_start, blk_end,
    296                                       body_blocks, dispatcher_factory)
    297         sub_irs.append(sub)

AttributeError: Failed at nopython (nopython frontend)
'object' object has no attribute 'mutate_with_body'

Copy link
Member Author

Choose a reason for hiding this comment

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

i think this should be fixed in this PR

@stuartarchibald
Copy link
Contributor

Thanks for the fixes @sklam I made a couple more comments but am of the view that this can be merged whilst acknowledging that there is more work to be done/this is experimental and incremental development is expected.

Copy link
Contributor

@stuartarchibald stuartarchibald left a comment

Choose a reason for hiding this comment

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

Thanks for the further fixes @sklam, one minor comment to resolve then this can be merged as convenient. Thanks again.

)
if not hasattr(ctxobj, 'mutate_with_body'):
raise errors.CompilerError(
"Unsupported use of contextmanager",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should that be Unsupported context manager in use, I think at this point the context manager statement has been identified and this is just duck type testing if it has an attr specific to Numba and is therefore supported?

Copy link
Member Author

Choose a reason for hiding this comment

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

Agree and fixed

@stuartarchibald
Copy link
Contributor

Thanks for 995fdda @sklam, this can be merged as convenient.

@stuartarchibald
Copy link
Contributor

@sklam tests are failing, think the error message update needs reflecting in the unit test catching the error. Thanks.

@sklam
Copy link
Member Author

sklam commented Aug 14, 2018

oops

@stuartarchibald
Copy link
Contributor

Thanks for fixing. Merging.

@stuartarchibald stuartarchibald merged commit 6555067 into numba:master Aug 14, 2018
@sklam sklam deleted the enh/withlifting branch August 23, 2018 16:02
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

4 participants