numba-cuda 0.19.1__py3-none-any.whl → 0.20.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of numba-cuda might be problematic. Click here for more details.
- numba_cuda/VERSION +1 -1
- numba_cuda/numba/cuda/__init__.py +1 -1
- numba_cuda/numba/cuda/_internal/cuda_bf16.py +12706 -1470
- numba_cuda/numba/cuda/_internal/cuda_fp16.py +2653 -8769
- numba_cuda/numba/cuda/api.py +6 -1
- numba_cuda/numba/cuda/bf16.py +285 -2
- numba_cuda/numba/cuda/cgutils.py +2 -2
- numba_cuda/numba/cuda/cloudpickle/__init__.py +21 -0
- numba_cuda/numba/cuda/cloudpickle/cloudpickle.py +1598 -0
- numba_cuda/numba/cuda/cloudpickle/cloudpickle_fast.py +17 -0
- numba_cuda/numba/cuda/codegen.py +1 -1
- numba_cuda/numba/cuda/compiler.py +373 -30
- numba_cuda/numba/cuda/core/analysis.py +319 -0
- numba_cuda/numba/cuda/core/annotations/__init__.py +0 -0
- numba_cuda/numba/cuda/core/annotations/type_annotations.py +304 -0
- numba_cuda/numba/cuda/core/base.py +1289 -0
- numba_cuda/numba/cuda/core/bytecode.py +727 -0
- numba_cuda/numba/cuda/core/caching.py +2 -2
- numba_cuda/numba/cuda/core/compiler.py +6 -14
- numba_cuda/numba/cuda/core/compiler_machinery.py +497 -0
- numba_cuda/numba/cuda/core/config.py +747 -0
- numba_cuda/numba/cuda/core/consts.py +124 -0
- numba_cuda/numba/cuda/core/cpu.py +370 -0
- numba_cuda/numba/cuda/core/environment.py +68 -0
- numba_cuda/numba/cuda/core/event.py +511 -0
- numba_cuda/numba/cuda/core/funcdesc.py +330 -0
- numba_cuda/numba/cuda/core/inline_closurecall.py +1889 -0
- numba_cuda/numba/cuda/core/interpreter.py +48 -26
- numba_cuda/numba/cuda/core/ir_utils.py +15 -26
- numba_cuda/numba/cuda/core/options.py +262 -0
- numba_cuda/numba/cuda/core/postproc.py +249 -0
- numba_cuda/numba/cuda/core/pythonapi.py +1868 -0
- numba_cuda/numba/cuda/core/rewrites/__init__.py +26 -0
- numba_cuda/numba/cuda/core/rewrites/ir_print.py +90 -0
- numba_cuda/numba/cuda/core/rewrites/registry.py +104 -0
- numba_cuda/numba/cuda/core/rewrites/static_binop.py +40 -0
- numba_cuda/numba/cuda/core/rewrites/static_getitem.py +187 -0
- numba_cuda/numba/cuda/core/rewrites/static_raise.py +98 -0
- numba_cuda/numba/cuda/core/ssa.py +496 -0
- numba_cuda/numba/cuda/core/targetconfig.py +329 -0
- numba_cuda/numba/cuda/core/tracing.py +231 -0
- numba_cuda/numba/cuda/core/transforms.py +952 -0
- numba_cuda/numba/cuda/core/typed_passes.py +738 -7
- numba_cuda/numba/cuda/core/typeinfer.py +1948 -0
- numba_cuda/numba/cuda/core/unsafe/__init__.py +0 -0
- numba_cuda/numba/cuda/core/unsafe/bytes.py +67 -0
- numba_cuda/numba/cuda/core/unsafe/eh.py +66 -0
- numba_cuda/numba/cuda/core/unsafe/refcount.py +98 -0
- numba_cuda/numba/cuda/core/untyped_passes.py +1983 -0
- numba_cuda/numba/cuda/cpython/cmathimpl.py +560 -0
- numba_cuda/numba/cuda/cpython/mathimpl.py +499 -0
- numba_cuda/numba/cuda/cpython/numbers.py +1474 -0
- numba_cuda/numba/cuda/cuda_paths.py +422 -246
- numba_cuda/numba/cuda/cudadecl.py +1 -1
- numba_cuda/numba/cuda/cudadrv/__init__.py +1 -1
- numba_cuda/numba/cuda/cudadrv/devicearray.py +2 -1
- numba_cuda/numba/cuda/cudadrv/driver.py +11 -140
- numba_cuda/numba/cuda/cudadrv/dummyarray.py +111 -24
- numba_cuda/numba/cuda/cudadrv/libs.py +5 -5
- numba_cuda/numba/cuda/cudadrv/mappings.py +1 -1
- numba_cuda/numba/cuda/cudadrv/nvrtc.py +19 -8
- numba_cuda/numba/cuda/cudadrv/nvvm.py +1 -4
- numba_cuda/numba/cuda/cudadrv/runtime.py +1 -1
- numba_cuda/numba/cuda/cudaimpl.py +5 -1
- numba_cuda/numba/cuda/debuginfo.py +85 -2
- numba_cuda/numba/cuda/decorators.py +3 -3
- numba_cuda/numba/cuda/descriptor.py +3 -4
- numba_cuda/numba/cuda/deviceufunc.py +66 -2
- numba_cuda/numba/cuda/dispatcher.py +18 -39
- numba_cuda/numba/cuda/flags.py +141 -1
- numba_cuda/numba/cuda/fp16.py +0 -2
- numba_cuda/numba/cuda/include/13/cuda_bf16.h +5118 -0
- numba_cuda/numba/cuda/include/13/cuda_bf16.hpp +3865 -0
- numba_cuda/numba/cuda/include/13/cuda_fp16.h +5363 -0
- numba_cuda/numba/cuda/include/13/cuda_fp16.hpp +3483 -0
- numba_cuda/numba/cuda/lowering.py +7 -144
- numba_cuda/numba/cuda/mathimpl.py +2 -1
- numba_cuda/numba/cuda/memory_management/nrt.py +43 -17
- numba_cuda/numba/cuda/misc/findlib.py +75 -0
- numba_cuda/numba/cuda/models.py +9 -1
- numba_cuda/numba/cuda/np/npdatetime_helpers.py +217 -0
- numba_cuda/numba/cuda/np/npyfuncs.py +1807 -0
- numba_cuda/numba/cuda/np/numpy_support.py +553 -0
- numba_cuda/numba/cuda/np/ufunc/ufuncbuilder.py +59 -0
- numba_cuda/numba/cuda/nvvmutils.py +1 -1
- numba_cuda/numba/cuda/printimpl.py +12 -1
- numba_cuda/numba/cuda/random.py +1 -1
- numba_cuda/numba/cuda/serialize.py +1 -1
- numba_cuda/numba/cuda/simulator/__init__.py +1 -1
- numba_cuda/numba/cuda/simulator/api.py +1 -1
- numba_cuda/numba/cuda/simulator/compiler.py +4 -0
- numba_cuda/numba/cuda/simulator/cudadrv/devicearray.py +1 -1
- numba_cuda/numba/cuda/simulator/kernelapi.py +1 -1
- numba_cuda/numba/cuda/simulator/memory_management/nrt.py +14 -2
- numba_cuda/numba/cuda/target.py +35 -17
- numba_cuda/numba/cuda/testing.py +7 -19
- numba_cuda/numba/cuda/tests/__init__.py +1 -1
- numba_cuda/numba/cuda/tests/cloudpickle_main_class.py +9 -0
- numba_cuda/numba/cuda/tests/core/test_serialize.py +4 -4
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_devicerecord.py +1 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_libraries.py +1 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_deallocations.py +1 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_detect.py +6 -3
- numba_cuda/numba/cuda/tests/cudadrv/test_emm_plugins.py +1 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_linker.py +18 -2
- numba_cuda/numba/cuda/tests/cudadrv/test_module_callbacks.py +2 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_nvjitlink.py +1 -1
- numba_cuda/numba/cuda/tests/cudadrv/test_ptds.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/extensions_usecases.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_array.py +2 -1
- numba_cuda/numba/cuda/tests/cudapy/test_atomics.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_bfloat16.py +539 -2
- numba_cuda/numba/cuda/tests/cudapy/test_bfloat16_bindings.py +81 -1
- numba_cuda/numba/cuda/tests/cudapy/test_caching.py +1 -3
- numba_cuda/numba/cuda/tests/cudapy/test_complex.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_constmem.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_cooperative_groups.py +2 -3
- numba_cuda/numba/cuda/tests/cudapy/test_copy_propagate.py +130 -0
- numba_cuda/numba/cuda/tests/cudapy/test_datetime.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_debug.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_debuginfo.py +293 -4
- numba_cuda/numba/cuda/tests/cudapy/test_debuginfo_types.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_dispatcher.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_errors.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_exception.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_extending.py +2 -1
- numba_cuda/numba/cuda/tests/cudapy/test_inline.py +18 -8
- numba_cuda/numba/cuda/tests/cudapy/test_intrinsics.py +23 -21
- numba_cuda/numba/cuda/tests/cudapy/test_ir_utils.py +10 -37
- numba_cuda/numba/cuda/tests/cudapy/test_laplace.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_math.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_matmul.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_operator.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_print.py +20 -0
- numba_cuda/numba/cuda/tests/cudapy/test_record_dtype.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_reduction.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_serialize.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_sm.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_ssa.py +453 -0
- numba_cuda/numba/cuda/tests/cudapy/test_sync.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_typeinfer.py +538 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ufuncs.py +263 -2
- numba_cuda/numba/cuda/tests/cudapy/test_userexc.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_vector_type.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize_decor.py +112 -6
- numba_cuda/numba/cuda/tests/cudapy/test_warning.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_warp_ops.py +1 -1
- numba_cuda/numba/cuda/tests/doc_examples/test_cg.py +0 -2
- numba_cuda/numba/cuda/tests/doc_examples/test_ffi.py +3 -2
- numba_cuda/numba/cuda/tests/doc_examples/test_laplace.py +0 -2
- numba_cuda/numba/cuda/tests/doc_examples/test_sessionize.py +0 -2
- numba_cuda/numba/cuda/tests/nocuda/test_import.py +3 -1
- numba_cuda/numba/cuda/tests/nocuda/test_library_lookup.py +24 -12
- numba_cuda/numba/cuda/tests/nrt/test_nrt.py +2 -1
- numba_cuda/numba/cuda/tests/support.py +55 -15
- numba_cuda/numba/cuda/tests/test_tracing.py +200 -0
- numba_cuda/numba/cuda/types.py +56 -0
- numba_cuda/numba/cuda/typing/__init__.py +9 -1
- numba_cuda/numba/cuda/typing/cffi_utils.py +55 -0
- numba_cuda/numba/cuda/typing/context.py +751 -0
- numba_cuda/numba/cuda/typing/enumdecl.py +74 -0
- numba_cuda/numba/cuda/typing/npydecl.py +658 -0
- numba_cuda/numba/cuda/typing/templates.py +7 -6
- numba_cuda/numba/cuda/ufuncs.py +3 -3
- numba_cuda/numba/cuda/utils.py +6 -112
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/METADATA +4 -3
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/RECORD +171 -116
- numba_cuda/numba/cuda/tests/cudadrv/test_mvc.py +0 -60
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/WHEEL +0 -0
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/licenses/LICENSE +0 -0
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/licenses/LICENSE.numba +0 -0
- {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1948 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
3
|
+
|
|
4
|
+
# ========================================================================== #
|
|
5
|
+
# #
|
|
6
|
+
# IMPORTANT: Before modifying type inference, it is important to port the #
|
|
7
|
+
# following tests from `numba.tests.test_typeinfer`: #
|
|
8
|
+
# #
|
|
9
|
+
# - TestArgRetCasting #
|
|
10
|
+
# - TestUnifyUseCases #
|
|
11
|
+
# - TestMiscIssues, with exceptions: #
|
|
12
|
+
# - test_list_unify{1,2}: Lists not supported #
|
|
13
|
+
# - test_load_fast_and_clear*: Uses list comprehensions #
|
|
14
|
+
# #
|
|
15
|
+
# ========================================================================== #
|
|
16
|
+
"""
|
|
17
|
+
Type inference base on CPA.
|
|
18
|
+
The algorithm guarantees monotonic growth of type-sets for each variable.
|
|
19
|
+
|
|
20
|
+
Steps:
|
|
21
|
+
1. seed initial types
|
|
22
|
+
2. build constraints
|
|
23
|
+
3. propagate constraints
|
|
24
|
+
4. unify types
|
|
25
|
+
|
|
26
|
+
Constraint propagation is precise and does not regret (no backtracing).
|
|
27
|
+
Constraints push types forward following the dataflow.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
import operator
|
|
32
|
+
import contextlib
|
|
33
|
+
import itertools
|
|
34
|
+
from pprint import pprint
|
|
35
|
+
from collections import OrderedDict, defaultdict
|
|
36
|
+
from functools import reduce
|
|
37
|
+
|
|
38
|
+
from numba.core import types, utils, typing, config, ir
|
|
39
|
+
from numba.core.typing.templates import Signature
|
|
40
|
+
from numba.core.errors import (
|
|
41
|
+
TypingError,
|
|
42
|
+
UntypedAttributeError,
|
|
43
|
+
new_error_context,
|
|
44
|
+
termcolor,
|
|
45
|
+
UnsupportedError,
|
|
46
|
+
ForceLiteralArg,
|
|
47
|
+
CompilerError,
|
|
48
|
+
NumbaValueError,
|
|
49
|
+
)
|
|
50
|
+
from numba.core.funcdesc import qualifying_prefix
|
|
51
|
+
from numba.core.typeconv import Conversion
|
|
52
|
+
|
|
53
|
+
_logger = logging.getLogger(__name__)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NOTSET:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# terminal color markup
|
|
61
|
+
_termcolor = termcolor()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TypeVar(object):
|
|
65
|
+
def __init__(self, context, var):
|
|
66
|
+
self.context = context
|
|
67
|
+
self.var = var
|
|
68
|
+
self.type = None
|
|
69
|
+
self.locked = False
|
|
70
|
+
# Stores source location of first definition
|
|
71
|
+
self.define_loc = None
|
|
72
|
+
# Qualifiers
|
|
73
|
+
self.literal_value = NOTSET
|
|
74
|
+
|
|
75
|
+
def add_type(self, tp, loc):
|
|
76
|
+
assert isinstance(tp, types.Type), type(tp)
|
|
77
|
+
# Special case for _undef_var.
|
|
78
|
+
# If the typevar is the _undef_var, use the incoming type directly.
|
|
79
|
+
if self.type is types._undef_var:
|
|
80
|
+
self.type = tp
|
|
81
|
+
return self.type
|
|
82
|
+
|
|
83
|
+
if self.locked:
|
|
84
|
+
if tp != self.type:
|
|
85
|
+
if self.context.can_convert(tp, self.type) is None:
|
|
86
|
+
msg = "No conversion from %s to %s for '%s', defined at %s"
|
|
87
|
+
raise TypingError(
|
|
88
|
+
msg % (tp, self.type, self.var, self.define_loc),
|
|
89
|
+
loc=loc,
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
if self.type is not None:
|
|
93
|
+
unified = self.context.unify_pairs(self.type, tp)
|
|
94
|
+
if unified is None:
|
|
95
|
+
msg = "Cannot unify %s and %s for '%s', defined at %s"
|
|
96
|
+
raise TypingError(
|
|
97
|
+
msg % (self.type, tp, self.var, self.define_loc),
|
|
98
|
+
loc=self.define_loc,
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
# First time definition
|
|
102
|
+
unified = tp
|
|
103
|
+
self.define_loc = loc
|
|
104
|
+
|
|
105
|
+
self.type = unified
|
|
106
|
+
|
|
107
|
+
return self.type
|
|
108
|
+
|
|
109
|
+
def lock(self, tp, loc, literal_value=NOTSET):
|
|
110
|
+
assert isinstance(tp, types.Type), type(tp)
|
|
111
|
+
|
|
112
|
+
if self.locked:
|
|
113
|
+
msg = (
|
|
114
|
+
"Invalid reassignment of a type-variable detected, type "
|
|
115
|
+
"variables are locked according to the user provided "
|
|
116
|
+
"function signature or from an ir.Const node. This is a "
|
|
117
|
+
"bug! Type={}. {}"
|
|
118
|
+
).format(tp, self.type)
|
|
119
|
+
raise CompilerError(msg, loc)
|
|
120
|
+
|
|
121
|
+
# If there is already a type, ensure we can convert it to the
|
|
122
|
+
# locked type.
|
|
123
|
+
if (
|
|
124
|
+
self.type is not None
|
|
125
|
+
and self.context.can_convert(self.type, tp) is None
|
|
126
|
+
):
|
|
127
|
+
raise TypingError(
|
|
128
|
+
"No conversion from %s to %s for "
|
|
129
|
+
"'%s'" % (tp, self.type, self.var),
|
|
130
|
+
loc=loc,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
self.type = tp
|
|
134
|
+
self.locked = True
|
|
135
|
+
if self.define_loc is None:
|
|
136
|
+
self.define_loc = loc
|
|
137
|
+
self.literal_value = literal_value
|
|
138
|
+
|
|
139
|
+
def union(self, other, loc):
|
|
140
|
+
if other.type is not None:
|
|
141
|
+
self.add_type(other.type, loc=loc)
|
|
142
|
+
|
|
143
|
+
return self.type
|
|
144
|
+
|
|
145
|
+
def __repr__(self):
|
|
146
|
+
return "%s := %s" % (self.var, self.type or "<undecided>")
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def defined(self):
|
|
150
|
+
return self.type is not None
|
|
151
|
+
|
|
152
|
+
def get(self):
|
|
153
|
+
return (self.type,) if self.type is not None else ()
|
|
154
|
+
|
|
155
|
+
def getone(self):
|
|
156
|
+
if self.type is None:
|
|
157
|
+
raise TypingError("Undecided type {}".format(self))
|
|
158
|
+
return self.type
|
|
159
|
+
|
|
160
|
+
def __len__(self):
|
|
161
|
+
return 1 if self.type is not None else 0
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class ConstraintNetwork(object):
|
|
165
|
+
"""
|
|
166
|
+
TODO: It is possible to optimize constraint propagation to consider only
|
|
167
|
+
dirty type variables.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
def __init__(self):
|
|
171
|
+
self.constraints = []
|
|
172
|
+
|
|
173
|
+
def append(self, constraint):
|
|
174
|
+
self.constraints.append(constraint)
|
|
175
|
+
|
|
176
|
+
def propagate(self, typeinfer):
|
|
177
|
+
"""
|
|
178
|
+
Execute all constraints. Errors are caught and returned as a list.
|
|
179
|
+
This allows progressing even though some constraints may fail
|
|
180
|
+
due to lack of information
|
|
181
|
+
(e.g. imprecise types such as List(undefined)).
|
|
182
|
+
"""
|
|
183
|
+
errors = []
|
|
184
|
+
for constraint in self.constraints:
|
|
185
|
+
loc = constraint.loc
|
|
186
|
+
with typeinfer.warnings.catch_warnings(
|
|
187
|
+
filename=loc.filename, lineno=loc.line
|
|
188
|
+
):
|
|
189
|
+
try:
|
|
190
|
+
constraint(typeinfer)
|
|
191
|
+
except ForceLiteralArg as e:
|
|
192
|
+
errors.append(e)
|
|
193
|
+
except TypingError as e:
|
|
194
|
+
_logger.debug("captured error", exc_info=e)
|
|
195
|
+
new_exc = TypingError(
|
|
196
|
+
str(e),
|
|
197
|
+
loc=constraint.loc,
|
|
198
|
+
highlighting=False,
|
|
199
|
+
)
|
|
200
|
+
errors.append(utils.chain_exception(new_exc, e))
|
|
201
|
+
|
|
202
|
+
return errors
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class Propagate(object):
|
|
206
|
+
"""
|
|
207
|
+
A simple constraint for direct propagation of types for assignments.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(self, dst, src, loc):
|
|
211
|
+
self.dst = dst
|
|
212
|
+
self.src = src
|
|
213
|
+
self.loc = loc
|
|
214
|
+
|
|
215
|
+
def __call__(self, typeinfer):
|
|
216
|
+
with new_error_context(
|
|
217
|
+
"typing of assignment at {0}", self.loc, loc=self.loc
|
|
218
|
+
):
|
|
219
|
+
typeinfer.copy_type(self.src, self.dst, loc=self.loc)
|
|
220
|
+
# If `dst` is refined, notify us
|
|
221
|
+
typeinfer.refine_map[self.dst] = self
|
|
222
|
+
|
|
223
|
+
def refine(self, typeinfer, target_type):
|
|
224
|
+
# Do not back-propagate to locked variables (e.g. constants)
|
|
225
|
+
assert target_type.is_precise()
|
|
226
|
+
typeinfer.add_type(
|
|
227
|
+
self.src, target_type, unless_locked=True, loc=self.loc
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class ArgConstraint(object):
|
|
232
|
+
def __init__(self, dst, src, loc):
|
|
233
|
+
self.dst = dst
|
|
234
|
+
self.src = src
|
|
235
|
+
self.loc = loc
|
|
236
|
+
|
|
237
|
+
def __call__(self, typeinfer):
|
|
238
|
+
with new_error_context("typing of argument at {0}", self.loc):
|
|
239
|
+
typevars = typeinfer.typevars
|
|
240
|
+
src = typevars[self.src]
|
|
241
|
+
if not src.defined:
|
|
242
|
+
return
|
|
243
|
+
ty = src.getone()
|
|
244
|
+
if isinstance(ty, types.Omitted):
|
|
245
|
+
ty = typeinfer.context.resolve_value_type_prefer_literal(
|
|
246
|
+
ty.value,
|
|
247
|
+
)
|
|
248
|
+
if not ty.is_precise():
|
|
249
|
+
raise TypingError("non-precise type {}".format(ty))
|
|
250
|
+
typeinfer.add_type(self.dst, ty, loc=self.loc)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class BuildTupleConstraint(object):
|
|
254
|
+
def __init__(self, target, items, loc):
|
|
255
|
+
self.target = target
|
|
256
|
+
self.items = items
|
|
257
|
+
self.loc = loc
|
|
258
|
+
|
|
259
|
+
def __call__(self, typeinfer):
|
|
260
|
+
with new_error_context("typing of tuple at {0}", self.loc):
|
|
261
|
+
typevars = typeinfer.typevars
|
|
262
|
+
tsets = [typevars[i.name].get() for i in self.items]
|
|
263
|
+
for vals in itertools.product(*tsets):
|
|
264
|
+
if vals and all(vals[0] == v for v in vals):
|
|
265
|
+
tup = types.UniTuple(dtype=vals[0], count=len(vals))
|
|
266
|
+
else:
|
|
267
|
+
# empty tuples fall here as well
|
|
268
|
+
tup = types.Tuple(vals)
|
|
269
|
+
assert tup.is_precise()
|
|
270
|
+
typeinfer.add_type(self.target, tup, loc=self.loc)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class _BuildContainerConstraint(object):
|
|
274
|
+
def __init__(self, target, items, loc):
|
|
275
|
+
self.target = target
|
|
276
|
+
self.items = items
|
|
277
|
+
self.loc = loc
|
|
278
|
+
|
|
279
|
+
def __call__(self, typeinfer):
|
|
280
|
+
with new_error_context(
|
|
281
|
+
"typing of {0} at {1}", self.container_type, self.loc
|
|
282
|
+
):
|
|
283
|
+
typevars = typeinfer.typevars
|
|
284
|
+
tsets = [typevars[i.name].get() for i in self.items]
|
|
285
|
+
if not tsets:
|
|
286
|
+
typeinfer.add_type(
|
|
287
|
+
self.target,
|
|
288
|
+
self.container_type(types.undefined),
|
|
289
|
+
loc=self.loc,
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
for typs in itertools.product(*tsets):
|
|
293
|
+
unified = typeinfer.context.unify_types(*typs)
|
|
294
|
+
if unified is not None:
|
|
295
|
+
typeinfer.add_type(
|
|
296
|
+
self.target,
|
|
297
|
+
self.container_type(unified),
|
|
298
|
+
loc=self.loc,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class BuildListConstraint(_BuildContainerConstraint):
|
|
303
|
+
def __init__(self, target, items, loc):
|
|
304
|
+
self.target = target
|
|
305
|
+
self.items = items
|
|
306
|
+
self.loc = loc
|
|
307
|
+
|
|
308
|
+
def __call__(self, typeinfer):
|
|
309
|
+
with new_error_context("typing of {0} at {1}", types.List, self.loc):
|
|
310
|
+
typevars = typeinfer.typevars
|
|
311
|
+
tsets = [typevars[i.name].get() for i in self.items]
|
|
312
|
+
if not tsets:
|
|
313
|
+
typeinfer.add_type(
|
|
314
|
+
self.target, types.List(types.undefined), loc=self.loc
|
|
315
|
+
)
|
|
316
|
+
else:
|
|
317
|
+
for typs in itertools.product(*tsets):
|
|
318
|
+
unified = typeinfer.context.unify_types(*typs)
|
|
319
|
+
if unified is not None:
|
|
320
|
+
# pull out literals if available
|
|
321
|
+
islit = [isinstance(x, types.Literal) for x in typs]
|
|
322
|
+
iv = None
|
|
323
|
+
if all(islit):
|
|
324
|
+
iv = [x.literal_value for x in typs]
|
|
325
|
+
typeinfer.add_type(
|
|
326
|
+
self.target,
|
|
327
|
+
types.List(unified, initial_value=iv),
|
|
328
|
+
loc=self.loc,
|
|
329
|
+
)
|
|
330
|
+
else:
|
|
331
|
+
typeinfer.add_type(
|
|
332
|
+
self.target, types.LiteralList(typs), loc=self.loc
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class BuildSetConstraint(_BuildContainerConstraint):
|
|
337
|
+
container_type = types.Set
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class BuildMapConstraint(object):
|
|
341
|
+
def __init__(self, target, items, special_value, value_indexes, loc):
|
|
342
|
+
self.target = target
|
|
343
|
+
self.items = items
|
|
344
|
+
self.special_value = special_value
|
|
345
|
+
self.value_indexes = value_indexes
|
|
346
|
+
self.loc = loc
|
|
347
|
+
|
|
348
|
+
def __call__(self, typeinfer):
|
|
349
|
+
with new_error_context("typing of dict at {0}", self.loc):
|
|
350
|
+
typevars = typeinfer.typevars
|
|
351
|
+
|
|
352
|
+
# figure out what sort of dict is being dealt with
|
|
353
|
+
tsets = [
|
|
354
|
+
(typevars[k.name].getone(), typevars[v.name].getone())
|
|
355
|
+
for k, v in self.items
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
if not tsets:
|
|
359
|
+
typeinfer.add_type(
|
|
360
|
+
self.target,
|
|
361
|
+
types.DictType(
|
|
362
|
+
types.undefined, types.undefined, self.special_value
|
|
363
|
+
),
|
|
364
|
+
loc=self.loc,
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
# all the info is known about the dict, if its
|
|
368
|
+
# str keys -> random heterogeneous values treat as literalstrkey
|
|
369
|
+
ktys = [x[0] for x in tsets]
|
|
370
|
+
vtys = [x[1] for x in tsets]
|
|
371
|
+
strkey = all([isinstance(x, types.StringLiteral) for x in ktys])
|
|
372
|
+
literalvty = all([isinstance(x, types.Literal) for x in vtys])
|
|
373
|
+
vt0 = types.unliteral(vtys[0])
|
|
374
|
+
|
|
375
|
+
# homogeneous values comes in the form of being able to cast
|
|
376
|
+
# all the other values in the ctor to the type of the first.
|
|
377
|
+
# The order is important as `typed.Dict` takes it's type from
|
|
378
|
+
# the first element.
|
|
379
|
+
def check(other):
|
|
380
|
+
conv = typeinfer.context.can_convert(other, vt0)
|
|
381
|
+
return conv is not None and conv < Conversion.unsafe
|
|
382
|
+
|
|
383
|
+
homogeneous = all([check(types.unliteral(x)) for x in vtys])
|
|
384
|
+
|
|
385
|
+
# Special cases:
|
|
386
|
+
# Single key:value in ctor, key is str, value is an otherwise
|
|
387
|
+
# illegal container type, e.g. LiteralStrKeyDict or
|
|
388
|
+
# List, there's no way to put this into a typed.Dict, so make it
|
|
389
|
+
# a LiteralStrKeyDict, same goes for LiteralList.
|
|
390
|
+
if len(vtys) == 1:
|
|
391
|
+
valty = vtys[0]
|
|
392
|
+
if isinstance(
|
|
393
|
+
valty,
|
|
394
|
+
(
|
|
395
|
+
types.LiteralStrKeyDict,
|
|
396
|
+
types.List,
|
|
397
|
+
types.LiteralList,
|
|
398
|
+
),
|
|
399
|
+
):
|
|
400
|
+
homogeneous = False
|
|
401
|
+
|
|
402
|
+
if strkey and not homogeneous:
|
|
403
|
+
resolved_dict = {x: y for x, y in zip(ktys, vtys)}
|
|
404
|
+
ty = types.LiteralStrKeyDict(
|
|
405
|
+
resolved_dict, self.value_indexes
|
|
406
|
+
)
|
|
407
|
+
typeinfer.add_type(self.target, ty, loc=self.loc)
|
|
408
|
+
else:
|
|
409
|
+
init_value = self.special_value if literalvty else None
|
|
410
|
+
key_type, value_type = tsets[0]
|
|
411
|
+
typeinfer.add_type(
|
|
412
|
+
self.target,
|
|
413
|
+
types.DictType(key_type, value_type, init_value),
|
|
414
|
+
loc=self.loc,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
class ExhaustIterConstraint(object):
|
|
419
|
+
def __init__(self, target, count, iterator, loc):
|
|
420
|
+
self.target = target
|
|
421
|
+
self.count = count
|
|
422
|
+
self.iterator = iterator
|
|
423
|
+
self.loc = loc
|
|
424
|
+
|
|
425
|
+
def __call__(self, typeinfer):
|
|
426
|
+
with new_error_context("typing of exhaust iter at {0}", self.loc):
|
|
427
|
+
typevars = typeinfer.typevars
|
|
428
|
+
for tp in typevars[self.iterator.name].get():
|
|
429
|
+
# unpack optional
|
|
430
|
+
tp = tp.type if isinstance(tp, types.Optional) else tp
|
|
431
|
+
if isinstance(tp, types.BaseTuple):
|
|
432
|
+
if len(tp) == self.count:
|
|
433
|
+
assert tp.is_precise()
|
|
434
|
+
typeinfer.add_type(self.target, tp, loc=self.loc)
|
|
435
|
+
break
|
|
436
|
+
else:
|
|
437
|
+
msg = (
|
|
438
|
+
f"wrong tuple length for {self.iterator.name}: ",
|
|
439
|
+
f"expected {self.count}, got {len(tp)}",
|
|
440
|
+
)
|
|
441
|
+
raise NumbaValueError(msg)
|
|
442
|
+
elif isinstance(tp, types.IterableType):
|
|
443
|
+
tup = types.UniTuple(
|
|
444
|
+
dtype=tp.iterator_type.yield_type, count=self.count
|
|
445
|
+
)
|
|
446
|
+
assert tup.is_precise()
|
|
447
|
+
typeinfer.add_type(self.target, tup, loc=self.loc)
|
|
448
|
+
break
|
|
449
|
+
else:
|
|
450
|
+
raise TypingError(
|
|
451
|
+
"failed to unpack {}".format(tp), loc=self.loc
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
class PairFirstConstraint(object):
|
|
456
|
+
def __init__(self, target, pair, loc):
|
|
457
|
+
self.target = target
|
|
458
|
+
self.pair = pair
|
|
459
|
+
self.loc = loc
|
|
460
|
+
|
|
461
|
+
def __call__(self, typeinfer):
|
|
462
|
+
with new_error_context("typing of pair-first at {0}", self.loc):
|
|
463
|
+
typevars = typeinfer.typevars
|
|
464
|
+
for tp in typevars[self.pair.name].get():
|
|
465
|
+
if not isinstance(tp, types.Pair):
|
|
466
|
+
# XXX is this an error?
|
|
467
|
+
continue
|
|
468
|
+
assert (
|
|
469
|
+
isinstance(tp.first_type, types.UndefinedFunctionType)
|
|
470
|
+
or tp.first_type.is_precise()
|
|
471
|
+
)
|
|
472
|
+
typeinfer.add_type(self.target, tp.first_type, loc=self.loc)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class PairSecondConstraint(object):
|
|
476
|
+
def __init__(self, target, pair, loc):
|
|
477
|
+
self.target = target
|
|
478
|
+
self.pair = pair
|
|
479
|
+
self.loc = loc
|
|
480
|
+
|
|
481
|
+
def __call__(self, typeinfer):
|
|
482
|
+
with new_error_context("typing of pair-second at {0}", self.loc):
|
|
483
|
+
typevars = typeinfer.typevars
|
|
484
|
+
for tp in typevars[self.pair.name].get():
|
|
485
|
+
if not isinstance(tp, types.Pair):
|
|
486
|
+
# XXX is this an error?
|
|
487
|
+
continue
|
|
488
|
+
assert tp.second_type.is_precise()
|
|
489
|
+
typeinfer.add_type(self.target, tp.second_type, loc=self.loc)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class StaticGetItemConstraint(object):
|
|
493
|
+
def __init__(self, target, value, index, index_var, loc):
|
|
494
|
+
self.target = target
|
|
495
|
+
self.value = value
|
|
496
|
+
self.index = index
|
|
497
|
+
if index_var is not None:
|
|
498
|
+
self.fallback = IntrinsicCallConstraint(
|
|
499
|
+
target, operator.getitem, (value, index_var), {}, None, loc
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
self.fallback = None
|
|
503
|
+
self.loc = loc
|
|
504
|
+
|
|
505
|
+
def __call__(self, typeinfer):
|
|
506
|
+
with new_error_context("typing of static-get-item at {0}", self.loc):
|
|
507
|
+
typevars = typeinfer.typevars
|
|
508
|
+
for ty in typevars[self.value.name].get():
|
|
509
|
+
sig = typeinfer.context.resolve_static_getitem(
|
|
510
|
+
value=ty,
|
|
511
|
+
index=self.index,
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
if sig is not None:
|
|
515
|
+
itemty = sig.return_type
|
|
516
|
+
# if the itemty is not precise, let it through, unification
|
|
517
|
+
# will catch it and produce a better error message
|
|
518
|
+
typeinfer.add_type(self.target, itemty, loc=self.loc)
|
|
519
|
+
elif self.fallback is not None:
|
|
520
|
+
self.fallback(typeinfer)
|
|
521
|
+
|
|
522
|
+
def get_call_signature(self):
|
|
523
|
+
# The signature is only needed for the fallback case in lowering
|
|
524
|
+
return self.fallback and self.fallback.get_call_signature()
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
class TypedGetItemConstraint(object):
|
|
528
|
+
def __init__(self, target, value, dtype, index, loc):
|
|
529
|
+
self.target = target
|
|
530
|
+
self.value = value
|
|
531
|
+
self.dtype = dtype
|
|
532
|
+
self.index = index
|
|
533
|
+
self.loc = loc
|
|
534
|
+
|
|
535
|
+
def __call__(self, typeinfer):
|
|
536
|
+
with new_error_context("typing of typed-get-item at {0}", self.loc):
|
|
537
|
+
typevars = typeinfer.typevars
|
|
538
|
+
idx_ty = typevars[self.index.name].get()
|
|
539
|
+
ty = typevars[self.value.name].get()
|
|
540
|
+
self.signature = Signature(self.dtype, ty + idx_ty, None)
|
|
541
|
+
typeinfer.add_type(self.target, self.dtype, loc=self.loc)
|
|
542
|
+
|
|
543
|
+
def get_call_signature(self):
|
|
544
|
+
return self.signature
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def fold_arg_vars(typevars, args, vararg, kws):
|
|
548
|
+
"""
|
|
549
|
+
Fold and resolve the argument variables of a function call.
|
|
550
|
+
"""
|
|
551
|
+
# Fetch all argument types, bail if any is unknown
|
|
552
|
+
n_pos_args = len(args)
|
|
553
|
+
kwds = [kw for (kw, var) in kws]
|
|
554
|
+
argtypes = [typevars[a.name] for a in args]
|
|
555
|
+
argtypes += [typevars[var.name] for (kw, var) in kws]
|
|
556
|
+
if vararg is not None:
|
|
557
|
+
argtypes.append(typevars[vararg.name])
|
|
558
|
+
|
|
559
|
+
if not all(a.defined for a in argtypes):
|
|
560
|
+
return
|
|
561
|
+
|
|
562
|
+
args = tuple(a.getone() for a in argtypes)
|
|
563
|
+
|
|
564
|
+
pos_args = args[:n_pos_args]
|
|
565
|
+
if vararg is not None:
|
|
566
|
+
errmsg = "*args in function call should be a tuple, got %s"
|
|
567
|
+
# Handle constant literal used for `*args`
|
|
568
|
+
if isinstance(args[-1], types.Literal):
|
|
569
|
+
const_val = args[-1].literal_value
|
|
570
|
+
# Is the constant value a tuple?
|
|
571
|
+
if not isinstance(const_val, tuple):
|
|
572
|
+
raise TypingError(errmsg % (args[-1],))
|
|
573
|
+
# Append the elements in the const tuple to the positional args
|
|
574
|
+
pos_args += const_val
|
|
575
|
+
# Handle non-constant
|
|
576
|
+
elif not isinstance(args[-1], types.BaseTuple):
|
|
577
|
+
# Unsuitable for *args
|
|
578
|
+
# (Python is more lenient and accepts all iterables)
|
|
579
|
+
raise TypingError(errmsg % (args[-1],))
|
|
580
|
+
else:
|
|
581
|
+
# Append the elements in the tuple to the positional args
|
|
582
|
+
pos_args += args[-1].types
|
|
583
|
+
# Drop the last arg
|
|
584
|
+
args = args[:-1]
|
|
585
|
+
kw_args = dict(zip(kwds, args[n_pos_args:]))
|
|
586
|
+
return pos_args, kw_args
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _is_array_not_precise(arrty):
|
|
590
|
+
"""Check type is array and it is not precise"""
|
|
591
|
+
return isinstance(arrty, types.Array) and not arrty.is_precise()
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
class CallConstraint(object):
|
|
595
|
+
"""Constraint for calling functions.
|
|
596
|
+
Perform case analysis foreach combinations of argument types.
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
signature = None
|
|
600
|
+
|
|
601
|
+
def __init__(self, target, func, args, kws, vararg, loc):
|
|
602
|
+
self.target = target
|
|
603
|
+
self.func = func
|
|
604
|
+
self.args = args
|
|
605
|
+
self.kws = kws or {}
|
|
606
|
+
self.vararg = vararg
|
|
607
|
+
self.loc = loc
|
|
608
|
+
|
|
609
|
+
def __call__(self, typeinfer):
|
|
610
|
+
msg = "typing of call at {0}\n".format(self.loc)
|
|
611
|
+
with new_error_context(msg):
|
|
612
|
+
typevars = typeinfer.typevars
|
|
613
|
+
with new_error_context(
|
|
614
|
+
"resolving caller type: {}".format(self.func)
|
|
615
|
+
):
|
|
616
|
+
fnty = typevars[self.func].getone()
|
|
617
|
+
with new_error_context("resolving callee type: {0}", fnty):
|
|
618
|
+
self.resolve(typeinfer, typevars, fnty)
|
|
619
|
+
|
|
620
|
+
def resolve(self, typeinfer, typevars, fnty):
|
|
621
|
+
assert fnty
|
|
622
|
+
context = typeinfer.context
|
|
623
|
+
|
|
624
|
+
r = fold_arg_vars(typevars, self.args, self.vararg, self.kws)
|
|
625
|
+
if r is None:
|
|
626
|
+
# Cannot resolve call type until all argument types are known
|
|
627
|
+
return
|
|
628
|
+
pos_args, kw_args = r
|
|
629
|
+
|
|
630
|
+
# Check argument to be precise
|
|
631
|
+
for a in itertools.chain(pos_args, kw_args.values()):
|
|
632
|
+
# Forbids imprecise type except array of undefined dtype
|
|
633
|
+
if not a.is_precise() and not isinstance(a, types.Array):
|
|
634
|
+
return
|
|
635
|
+
|
|
636
|
+
# Resolve call type
|
|
637
|
+
if isinstance(fnty, types.TypeRef):
|
|
638
|
+
# Unwrap TypeRef
|
|
639
|
+
fnty = fnty.instance_type
|
|
640
|
+
try:
|
|
641
|
+
sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
|
|
642
|
+
except ForceLiteralArg as e:
|
|
643
|
+
# Adjust for bound methods
|
|
644
|
+
folding_args = (
|
|
645
|
+
(fnty.this,) + tuple(self.args)
|
|
646
|
+
if isinstance(fnty, types.BoundFunction)
|
|
647
|
+
else self.args
|
|
648
|
+
)
|
|
649
|
+
folded = e.fold_arguments(folding_args, self.kws)
|
|
650
|
+
requested = set()
|
|
651
|
+
unsatisfied = set()
|
|
652
|
+
for idx in e.requested_args:
|
|
653
|
+
maybe_arg = typeinfer.func_ir.get_definition(folded[idx])
|
|
654
|
+
if isinstance(maybe_arg, ir.Arg):
|
|
655
|
+
requested.add(maybe_arg.index)
|
|
656
|
+
else:
|
|
657
|
+
unsatisfied.add(idx)
|
|
658
|
+
if unsatisfied:
|
|
659
|
+
raise TypingError("Cannot request literal type.", loc=self.loc)
|
|
660
|
+
elif requested:
|
|
661
|
+
raise ForceLiteralArg(requested, loc=self.loc)
|
|
662
|
+
if sig is None:
|
|
663
|
+
# Note: duplicated error checking.
|
|
664
|
+
# See types.BaseFunction.get_call_type
|
|
665
|
+
# Arguments are invalid => explain why
|
|
666
|
+
headtemp = "Invalid use of {0} with parameters ({1})"
|
|
667
|
+
args = [str(a) for a in pos_args]
|
|
668
|
+
args += ["%s=%s" % (k, v) for k, v in sorted(kw_args.items())]
|
|
669
|
+
head = headtemp.format(fnty, ", ".join(map(str, args)))
|
|
670
|
+
desc = context.explain_function_type(fnty)
|
|
671
|
+
msg = "\n".join([head, desc])
|
|
672
|
+
raise TypingError(msg)
|
|
673
|
+
|
|
674
|
+
typeinfer.add_type(self.target, sig.return_type, loc=self.loc)
|
|
675
|
+
|
|
676
|
+
# If the function is a bound function and its receiver type
|
|
677
|
+
# was refined, propagate it.
|
|
678
|
+
if (
|
|
679
|
+
isinstance(fnty, types.BoundFunction)
|
|
680
|
+
and sig.recvr is not None
|
|
681
|
+
and sig.recvr != fnty.this
|
|
682
|
+
):
|
|
683
|
+
refined_this = context.unify_pairs(sig.recvr, fnty.this)
|
|
684
|
+
if (
|
|
685
|
+
refined_this is None
|
|
686
|
+
and fnty.this.is_precise()
|
|
687
|
+
and sig.recvr.is_precise()
|
|
688
|
+
):
|
|
689
|
+
msg = "Cannot refine type {} to {}".format(
|
|
690
|
+
sig.recvr,
|
|
691
|
+
fnty.this,
|
|
692
|
+
)
|
|
693
|
+
raise TypingError(msg, loc=self.loc)
|
|
694
|
+
if refined_this is not None and refined_this.is_precise():
|
|
695
|
+
refined_fnty = fnty.copy(this=refined_this)
|
|
696
|
+
typeinfer.propagate_refined_type(self.func, refined_fnty)
|
|
697
|
+
|
|
698
|
+
# If the return type is imprecise but can be unified with the
|
|
699
|
+
# target variable's inferred type, use the latter.
|
|
700
|
+
# Useful for code such as::
|
|
701
|
+
# s = set()
|
|
702
|
+
# s.add(1)
|
|
703
|
+
# (the set() call must be typed as int64(), not undefined())
|
|
704
|
+
if not sig.return_type.is_precise():
|
|
705
|
+
target = typevars[self.target]
|
|
706
|
+
if target.defined:
|
|
707
|
+
targetty = target.getone()
|
|
708
|
+
if context.unify_pairs(targetty, sig.return_type) == targetty:
|
|
709
|
+
sig = sig.replace(return_type=targetty)
|
|
710
|
+
|
|
711
|
+
self.signature = sig
|
|
712
|
+
self._add_refine_map(typeinfer, typevars, sig)
|
|
713
|
+
|
|
714
|
+
def _add_refine_map(self, typeinfer, typevars, sig):
|
|
715
|
+
"""Add this expression to the refine_map base on the type of target_type"""
|
|
716
|
+
target_type = typevars[self.target].getone()
|
|
717
|
+
# Array
|
|
718
|
+
if isinstance(target_type, types.Array) and isinstance(
|
|
719
|
+
sig.return_type.dtype, types.Undefined
|
|
720
|
+
):
|
|
721
|
+
typeinfer.refine_map[self.target] = self
|
|
722
|
+
# DictType
|
|
723
|
+
if (
|
|
724
|
+
isinstance(target_type, types.DictType)
|
|
725
|
+
and not target_type.is_precise()
|
|
726
|
+
):
|
|
727
|
+
typeinfer.refine_map[self.target] = self
|
|
728
|
+
|
|
729
|
+
def refine(self, typeinfer, updated_type):
|
|
730
|
+
# Is getitem?
|
|
731
|
+
if self.func == operator.getitem:
|
|
732
|
+
aryty = typeinfer.typevars[self.args[0].name].getone()
|
|
733
|
+
# is array not precise?
|
|
734
|
+
if _is_array_not_precise(aryty):
|
|
735
|
+
# allow refinement of dtype
|
|
736
|
+
assert updated_type.is_precise()
|
|
737
|
+
newtype = aryty.copy(dtype=updated_type.dtype)
|
|
738
|
+
typeinfer.add_type(self.args[0].name, newtype, loc=self.loc)
|
|
739
|
+
else:
|
|
740
|
+
m = "no type refinement implemented for function {} updating to {}"
|
|
741
|
+
raise TypingError(m.format(self.func, updated_type))
|
|
742
|
+
|
|
743
|
+
def get_call_signature(self):
|
|
744
|
+
return self.signature
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class IntrinsicCallConstraint(CallConstraint):
|
|
748
|
+
def __call__(self, typeinfer):
|
|
749
|
+
with new_error_context("typing of intrinsic-call at {0}", self.loc):
|
|
750
|
+
fnty = self.func
|
|
751
|
+
if fnty in utils.OPERATORS_TO_BUILTINS:
|
|
752
|
+
fnty = typeinfer.resolve_value_type(None, fnty)
|
|
753
|
+
self.resolve(typeinfer, typeinfer.typevars, fnty=fnty)
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
class GetAttrConstraint(object):
|
|
757
|
+
def __init__(self, target, attr, value, loc, inst):
|
|
758
|
+
self.target = target
|
|
759
|
+
self.attr = attr
|
|
760
|
+
self.value = value
|
|
761
|
+
self.loc = loc
|
|
762
|
+
self.inst = inst
|
|
763
|
+
|
|
764
|
+
def __call__(self, typeinfer):
|
|
765
|
+
with new_error_context("typing of get attribute at {0}", self.loc):
|
|
766
|
+
typevars = typeinfer.typevars
|
|
767
|
+
valtys = typevars[self.value.name].get()
|
|
768
|
+
for ty in valtys:
|
|
769
|
+
attrty = typeinfer.context.resolve_getattr(ty, self.attr)
|
|
770
|
+
if attrty is None:
|
|
771
|
+
raise UntypedAttributeError(
|
|
772
|
+
ty, self.attr, loc=self.inst.loc
|
|
773
|
+
)
|
|
774
|
+
else:
|
|
775
|
+
assert attrty.is_precise()
|
|
776
|
+
typeinfer.add_type(self.target, attrty, loc=self.loc)
|
|
777
|
+
typeinfer.refine_map[self.target] = self
|
|
778
|
+
|
|
779
|
+
def refine(self, typeinfer, target_type):
|
|
780
|
+
if isinstance(target_type, types.BoundFunction):
|
|
781
|
+
recvr = target_type.this
|
|
782
|
+
assert recvr.is_precise()
|
|
783
|
+
typeinfer.add_type(self.value.name, recvr, loc=self.loc)
|
|
784
|
+
source_constraint = typeinfer.refine_map.get(self.value.name)
|
|
785
|
+
if source_constraint is not None:
|
|
786
|
+
source_constraint.refine(typeinfer, recvr)
|
|
787
|
+
|
|
788
|
+
def __repr__(self):
|
|
789
|
+
return 'resolving type of attribute "{attr}" of "{value}"'.format(
|
|
790
|
+
value=self.value, attr=self.attr
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
class SetItemRefinement(object):
|
|
795
|
+
"""A mixin class to provide the common refinement logic in setitem
|
|
796
|
+
and static setitem.
|
|
797
|
+
"""
|
|
798
|
+
|
|
799
|
+
def _refine_target_type(self, typeinfer, targetty, idxty, valty, sig):
|
|
800
|
+
"""Refine the target-type given the known index type and value type."""
|
|
801
|
+
# For array setitem, refine imprecise array dtype
|
|
802
|
+
if _is_array_not_precise(targetty):
|
|
803
|
+
typeinfer.add_type(self.target.name, sig.args[0], loc=self.loc)
|
|
804
|
+
# For Dict setitem
|
|
805
|
+
if isinstance(targetty, types.DictType):
|
|
806
|
+
if not targetty.is_precise():
|
|
807
|
+
refined = targetty.refine(idxty, valty)
|
|
808
|
+
typeinfer.add_type(
|
|
809
|
+
self.target.name,
|
|
810
|
+
refined,
|
|
811
|
+
loc=self.loc,
|
|
812
|
+
)
|
|
813
|
+
elif isinstance(targetty, types.LiteralStrKeyDict):
|
|
814
|
+
typeinfer.add_type(
|
|
815
|
+
self.target.name,
|
|
816
|
+
types.DictType(idxty, valty),
|
|
817
|
+
loc=self.loc,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
class SetItemConstraint(SetItemRefinement):
|
|
822
|
+
def __init__(self, target, index, value, loc):
|
|
823
|
+
self.target = target
|
|
824
|
+
self.index = index
|
|
825
|
+
self.value = value
|
|
826
|
+
self.loc = loc
|
|
827
|
+
|
|
828
|
+
def __call__(self, typeinfer):
|
|
829
|
+
with new_error_context("typing of setitem at {0}", self.loc):
|
|
830
|
+
typevars = typeinfer.typevars
|
|
831
|
+
if not all(
|
|
832
|
+
typevars[var.name].defined
|
|
833
|
+
for var in (self.target, self.index, self.value)
|
|
834
|
+
):
|
|
835
|
+
return
|
|
836
|
+
targetty = typevars[self.target.name].getone()
|
|
837
|
+
idxty = typevars[self.index.name].getone()
|
|
838
|
+
valty = typevars[self.value.name].getone()
|
|
839
|
+
|
|
840
|
+
sig = typeinfer.context.resolve_setitem(targetty, idxty, valty)
|
|
841
|
+
if sig is None:
|
|
842
|
+
raise TypingError(
|
|
843
|
+
"Cannot resolve setitem: %s[%s] = %s"
|
|
844
|
+
% (targetty, idxty, valty),
|
|
845
|
+
loc=self.loc,
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
self.signature = sig
|
|
849
|
+
self._refine_target_type(typeinfer, targetty, idxty, valty, sig)
|
|
850
|
+
|
|
851
|
+
def get_call_signature(self):
|
|
852
|
+
return self.signature
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
class StaticSetItemConstraint(SetItemRefinement):
|
|
856
|
+
def __init__(self, target, index, index_var, value, loc):
|
|
857
|
+
self.target = target
|
|
858
|
+
self.index = index
|
|
859
|
+
self.index_var = index_var
|
|
860
|
+
self.value = value
|
|
861
|
+
self.loc = loc
|
|
862
|
+
|
|
863
|
+
def __call__(self, typeinfer):
|
|
864
|
+
with new_error_context("typing of staticsetitem at {0}", self.loc):
|
|
865
|
+
typevars = typeinfer.typevars
|
|
866
|
+
if not all(
|
|
867
|
+
typevars[var.name].defined
|
|
868
|
+
for var in (self.target, self.index_var, self.value)
|
|
869
|
+
):
|
|
870
|
+
return
|
|
871
|
+
targetty = typevars[self.target.name].getone()
|
|
872
|
+
idxty = typevars[self.index_var.name].getone()
|
|
873
|
+
valty = typevars[self.value.name].getone()
|
|
874
|
+
|
|
875
|
+
sig = typeinfer.context.resolve_static_setitem(
|
|
876
|
+
targetty, self.index, valty
|
|
877
|
+
)
|
|
878
|
+
if sig is None:
|
|
879
|
+
sig = typeinfer.context.resolve_setitem(targetty, idxty, valty)
|
|
880
|
+
if sig is None:
|
|
881
|
+
raise TypingError(
|
|
882
|
+
"Cannot resolve setitem: %s[%r] = %s"
|
|
883
|
+
% (targetty, self.index, valty),
|
|
884
|
+
loc=self.loc,
|
|
885
|
+
)
|
|
886
|
+
self.signature = sig
|
|
887
|
+
self._refine_target_type(typeinfer, targetty, idxty, valty, sig)
|
|
888
|
+
|
|
889
|
+
def get_call_signature(self):
|
|
890
|
+
return self.signature
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
class DelItemConstraint(object):
|
|
894
|
+
def __init__(self, target, index, loc):
|
|
895
|
+
self.target = target
|
|
896
|
+
self.index = index
|
|
897
|
+
self.loc = loc
|
|
898
|
+
|
|
899
|
+
def __call__(self, typeinfer):
|
|
900
|
+
with new_error_context("typing of delitem at {0}", self.loc):
|
|
901
|
+
typevars = typeinfer.typevars
|
|
902
|
+
if not all(
|
|
903
|
+
typevars[var.name].defined for var in (self.target, self.index)
|
|
904
|
+
):
|
|
905
|
+
return
|
|
906
|
+
targetty = typevars[self.target.name].getone()
|
|
907
|
+
idxty = typevars[self.index.name].getone()
|
|
908
|
+
|
|
909
|
+
sig = typeinfer.context.resolve_delitem(targetty, idxty)
|
|
910
|
+
if sig is None:
|
|
911
|
+
raise TypingError(
|
|
912
|
+
"Cannot resolve delitem: %s[%s]" % (targetty, idxty),
|
|
913
|
+
loc=self.loc,
|
|
914
|
+
)
|
|
915
|
+
self.signature = sig
|
|
916
|
+
|
|
917
|
+
def get_call_signature(self):
|
|
918
|
+
return self.signature
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
class SetAttrConstraint(object):
|
|
922
|
+
def __init__(self, target, attr, value, loc):
|
|
923
|
+
self.target = target
|
|
924
|
+
self.attr = attr
|
|
925
|
+
self.value = value
|
|
926
|
+
self.loc = loc
|
|
927
|
+
|
|
928
|
+
def __call__(self, typeinfer):
|
|
929
|
+
with new_error_context(
|
|
930
|
+
"typing of set attribute {0!r} at {1}", self.attr, self.loc
|
|
931
|
+
):
|
|
932
|
+
typevars = typeinfer.typevars
|
|
933
|
+
if not all(
|
|
934
|
+
typevars[var.name].defined for var in (self.target, self.value)
|
|
935
|
+
):
|
|
936
|
+
return
|
|
937
|
+
targetty = typevars[self.target.name].getone()
|
|
938
|
+
valty = typevars[self.value.name].getone()
|
|
939
|
+
sig = typeinfer.context.resolve_setattr(targetty, self.attr, valty)
|
|
940
|
+
if sig is None:
|
|
941
|
+
raise TypingError(
|
|
942
|
+
"Cannot resolve setattr: (%s).%s = %s"
|
|
943
|
+
% (targetty, self.attr, valty),
|
|
944
|
+
loc=self.loc,
|
|
945
|
+
)
|
|
946
|
+
self.signature = sig
|
|
947
|
+
|
|
948
|
+
def get_call_signature(self):
|
|
949
|
+
return self.signature
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
class PrintConstraint(object):
|
|
953
|
+
def __init__(self, args, vararg, loc):
|
|
954
|
+
self.args = args
|
|
955
|
+
self.vararg = vararg
|
|
956
|
+
self.loc = loc
|
|
957
|
+
|
|
958
|
+
def __call__(self, typeinfer):
|
|
959
|
+
typevars = typeinfer.typevars
|
|
960
|
+
|
|
961
|
+
r = fold_arg_vars(typevars, self.args, self.vararg, {})
|
|
962
|
+
if r is None:
|
|
963
|
+
# Cannot resolve call type until all argument types are known
|
|
964
|
+
return
|
|
965
|
+
pos_args, kw_args = r
|
|
966
|
+
|
|
967
|
+
fnty = typeinfer.context.resolve_value_type(print)
|
|
968
|
+
assert fnty is not None
|
|
969
|
+
sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
|
|
970
|
+
self.signature = sig
|
|
971
|
+
|
|
972
|
+
def get_call_signature(self):
|
|
973
|
+
return self.signature
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
class TypeVarMap(dict):
|
|
977
|
+
def set_context(self, context):
|
|
978
|
+
self.context = context
|
|
979
|
+
|
|
980
|
+
def __getitem__(self, name):
|
|
981
|
+
if name not in self:
|
|
982
|
+
self[name] = TypeVar(self.context, name)
|
|
983
|
+
return super(TypeVarMap, self).__getitem__(name)
|
|
984
|
+
|
|
985
|
+
def __setitem__(self, name, value):
|
|
986
|
+
assert isinstance(name, str)
|
|
987
|
+
if name in self:
|
|
988
|
+
raise KeyError("Cannot redefine typevar %s" % name)
|
|
989
|
+
else:
|
|
990
|
+
super(TypeVarMap, self).__setitem__(name, value)
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
# A temporary mapping of {function name: dispatcher object}
|
|
994
|
+
_temporary_dispatcher_map = {}
|
|
995
|
+
# A temporary mapping of {function name: dispatcher object reference count}
|
|
996
|
+
# Reference: https://github.com/numba/numba/issues/3658
|
|
997
|
+
_temporary_dispatcher_map_ref_count = defaultdict(int)
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
@contextlib.contextmanager
|
|
1001
|
+
def register_dispatcher(disp):
|
|
1002
|
+
"""
|
|
1003
|
+
Register a Dispatcher for inference while it is not yet stored
|
|
1004
|
+
as global or closure variable (e.g. during execution of the @jit()
|
|
1005
|
+
call). This allows resolution of recursive calls with eager
|
|
1006
|
+
compilation.
|
|
1007
|
+
"""
|
|
1008
|
+
assert callable(disp)
|
|
1009
|
+
assert callable(disp.py_func)
|
|
1010
|
+
name = disp.py_func.__name__
|
|
1011
|
+
_temporary_dispatcher_map[name] = disp
|
|
1012
|
+
_temporary_dispatcher_map_ref_count[name] += 1
|
|
1013
|
+
try:
|
|
1014
|
+
yield
|
|
1015
|
+
finally:
|
|
1016
|
+
_temporary_dispatcher_map_ref_count[name] -= 1
|
|
1017
|
+
if not _temporary_dispatcher_map_ref_count[name]:
|
|
1018
|
+
del _temporary_dispatcher_map[name]
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
typeinfer_extensions = {}
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
class TypeInferer(object):
|
|
1025
|
+
"""
|
|
1026
|
+
Operates on block that shares the same ir.Scope.
|
|
1027
|
+
"""
|
|
1028
|
+
|
|
1029
|
+
def __init__(self, context, func_ir, warnings):
|
|
1030
|
+
self.context = context
|
|
1031
|
+
# sort based on label, ensure iteration order!
|
|
1032
|
+
self.blocks = OrderedDict()
|
|
1033
|
+
for k in sorted(func_ir.blocks.keys()):
|
|
1034
|
+
self.blocks[k] = func_ir.blocks[k]
|
|
1035
|
+
self.generator_info = func_ir.generator_info
|
|
1036
|
+
self.func_id = func_ir.func_id
|
|
1037
|
+
self.func_ir = func_ir
|
|
1038
|
+
|
|
1039
|
+
self.typevars = TypeVarMap()
|
|
1040
|
+
self.typevars.set_context(context)
|
|
1041
|
+
self.constraints = ConstraintNetwork()
|
|
1042
|
+
self.warnings = warnings
|
|
1043
|
+
|
|
1044
|
+
# { index: mangled name }
|
|
1045
|
+
self.arg_names = {}
|
|
1046
|
+
# self.return_type = None
|
|
1047
|
+
# Set of assumed immutable globals
|
|
1048
|
+
self.assumed_immutables = set()
|
|
1049
|
+
# Track all calls and associated constraints
|
|
1050
|
+
self.calls = []
|
|
1051
|
+
# The inference result of the above calls
|
|
1052
|
+
self.calltypes = utils.UniqueDict()
|
|
1053
|
+
# Target var -> constraint with refine hook
|
|
1054
|
+
self.refine_map = {}
|
|
1055
|
+
|
|
1056
|
+
if config.DEBUG or config.DEBUG_TYPEINFER:
|
|
1057
|
+
self.debug = TypeInferDebug(self)
|
|
1058
|
+
else:
|
|
1059
|
+
self.debug = NullDebug()
|
|
1060
|
+
|
|
1061
|
+
self._skip_recursion = False
|
|
1062
|
+
|
|
1063
|
+
def copy(self, skip_recursion=False):
|
|
1064
|
+
clone = TypeInferer(self.context, self.func_ir, self.warnings)
|
|
1065
|
+
clone.arg_names = self.arg_names.copy()
|
|
1066
|
+
clone._skip_recursion = skip_recursion
|
|
1067
|
+
|
|
1068
|
+
for k, v in self.typevars.items():
|
|
1069
|
+
if not v.locked and v.defined:
|
|
1070
|
+
clone.typevars[k].add_type(v.getone(), loc=v.define_loc)
|
|
1071
|
+
|
|
1072
|
+
return clone
|
|
1073
|
+
|
|
1074
|
+
def _mangle_arg_name(self, name):
|
|
1075
|
+
# Disambiguise argument name
|
|
1076
|
+
return "arg.%s" % (name,)
|
|
1077
|
+
|
|
1078
|
+
def _get_return_vars(self):
|
|
1079
|
+
rets = []
|
|
1080
|
+
for blk in self.blocks.values():
|
|
1081
|
+
inst = blk.terminator
|
|
1082
|
+
if isinstance(inst, ir.Return):
|
|
1083
|
+
rets.append(inst.value)
|
|
1084
|
+
return rets
|
|
1085
|
+
|
|
1086
|
+
def get_argument_types(self):
|
|
1087
|
+
return [self.typevars[k].getone() for k in self.arg_names.values()]
|
|
1088
|
+
|
|
1089
|
+
def seed_argument(self, name, index, typ):
|
|
1090
|
+
name = self._mangle_arg_name(name)
|
|
1091
|
+
self.seed_type(name, typ)
|
|
1092
|
+
self.arg_names[index] = name
|
|
1093
|
+
|
|
1094
|
+
def seed_type(self, name, typ):
|
|
1095
|
+
"""All arguments should be seeded."""
|
|
1096
|
+
self.lock_type(name, typ, loc=None)
|
|
1097
|
+
|
|
1098
|
+
def seed_return(self, typ):
|
|
1099
|
+
"""Seeding of return value is optional."""
|
|
1100
|
+
for var in self._get_return_vars():
|
|
1101
|
+
self.lock_type(var.name, typ, loc=None)
|
|
1102
|
+
|
|
1103
|
+
def build_constraint(self):
|
|
1104
|
+
for blk in self.blocks.values():
|
|
1105
|
+
for inst in blk.body:
|
|
1106
|
+
self.constrain_statement(inst)
|
|
1107
|
+
|
|
1108
|
+
def return_types_from_partial(self):
|
|
1109
|
+
"""
|
|
1110
|
+
Resume type inference partially to deduce the return type.
|
|
1111
|
+
Note: No side-effect to `self`.
|
|
1112
|
+
|
|
1113
|
+
Returns the inferred return type or None if it cannot deduce the return
|
|
1114
|
+
type.
|
|
1115
|
+
"""
|
|
1116
|
+
# Clone the typeinferer and disable typing recursive calls
|
|
1117
|
+
cloned = self.copy(skip_recursion=True)
|
|
1118
|
+
# rebuild constraint network
|
|
1119
|
+
cloned.build_constraint()
|
|
1120
|
+
# propagate without raising
|
|
1121
|
+
cloned.propagate(raise_errors=False)
|
|
1122
|
+
# get return types
|
|
1123
|
+
rettypes = set()
|
|
1124
|
+
for retvar in cloned._get_return_vars():
|
|
1125
|
+
if retvar.name in cloned.typevars:
|
|
1126
|
+
typevar = cloned.typevars[retvar.name]
|
|
1127
|
+
if typevar and typevar.defined:
|
|
1128
|
+
rettypes.add(types.unliteral(typevar.getone()))
|
|
1129
|
+
if not rettypes:
|
|
1130
|
+
return
|
|
1131
|
+
# unify return types
|
|
1132
|
+
return cloned._unify_return_types(rettypes)
|
|
1133
|
+
|
|
1134
|
+
def propagate(self, raise_errors=True):
|
|
1135
|
+
newtoken = self.get_state_token()
|
|
1136
|
+
oldtoken = None
|
|
1137
|
+
# Since the number of types are finite, the typesets will eventually
|
|
1138
|
+
# stop growing.
|
|
1139
|
+
|
|
1140
|
+
while newtoken != oldtoken:
|
|
1141
|
+
self.debug.propagate_started()
|
|
1142
|
+
oldtoken = newtoken
|
|
1143
|
+
# Errors can appear when the type set is incomplete; only
|
|
1144
|
+
# raise them when there is no progress anymore.
|
|
1145
|
+
errors = self.constraints.propagate(self)
|
|
1146
|
+
newtoken = self.get_state_token()
|
|
1147
|
+
self.debug.propagate_finished()
|
|
1148
|
+
if errors:
|
|
1149
|
+
if raise_errors:
|
|
1150
|
+
force_lit_args = [
|
|
1151
|
+
e for e in errors if isinstance(e, ForceLiteralArg)
|
|
1152
|
+
]
|
|
1153
|
+
if not force_lit_args:
|
|
1154
|
+
raise errors[0]
|
|
1155
|
+
else:
|
|
1156
|
+
raise reduce(operator.or_, force_lit_args)
|
|
1157
|
+
else:
|
|
1158
|
+
return errors
|
|
1159
|
+
|
|
1160
|
+
def add_type(self, var, tp, loc, unless_locked=False):
|
|
1161
|
+
assert isinstance(var, str), type(var)
|
|
1162
|
+
tv = self.typevars[var]
|
|
1163
|
+
if unless_locked and tv.locked:
|
|
1164
|
+
return
|
|
1165
|
+
oldty = tv.type
|
|
1166
|
+
unified = tv.add_type(tp, loc=loc)
|
|
1167
|
+
if unified != oldty:
|
|
1168
|
+
self.propagate_refined_type(var, unified)
|
|
1169
|
+
|
|
1170
|
+
def add_calltype(self, inst, signature):
|
|
1171
|
+
assert signature is not None
|
|
1172
|
+
self.calltypes[inst] = signature
|
|
1173
|
+
|
|
1174
|
+
def copy_type(self, src_var, dest_var, loc):
|
|
1175
|
+
self.typevars[dest_var].union(self.typevars[src_var], loc=loc)
|
|
1176
|
+
|
|
1177
|
+
def lock_type(self, var, tp, loc, literal_value=NOTSET):
|
|
1178
|
+
tv = self.typevars[var]
|
|
1179
|
+
tv.lock(tp, loc=loc, literal_value=literal_value)
|
|
1180
|
+
|
|
1181
|
+
def propagate_refined_type(self, updated_var, updated_type):
|
|
1182
|
+
source_constraint = self.refine_map.get(updated_var)
|
|
1183
|
+
if source_constraint is not None:
|
|
1184
|
+
source_constraint.refine(self, updated_type)
|
|
1185
|
+
|
|
1186
|
+
def unify(self, raise_errors=True):
|
|
1187
|
+
"""
|
|
1188
|
+
Run the final unification pass over all inferred types, and
|
|
1189
|
+
catch imprecise types.
|
|
1190
|
+
"""
|
|
1191
|
+
typdict = utils.UniqueDict()
|
|
1192
|
+
|
|
1193
|
+
def find_offender(name, exhaustive=False):
|
|
1194
|
+
# finds the offending variable definition by name
|
|
1195
|
+
# if exhaustive is set it will try and trace through temporary
|
|
1196
|
+
# variables to find a concrete offending definition.
|
|
1197
|
+
offender = None
|
|
1198
|
+
for block in self.func_ir.blocks.values():
|
|
1199
|
+
offender = block.find_variable_assignment(name)
|
|
1200
|
+
if offender is not None:
|
|
1201
|
+
if not exhaustive:
|
|
1202
|
+
break
|
|
1203
|
+
try: # simple assignment
|
|
1204
|
+
hasattr(offender.value, "name")
|
|
1205
|
+
offender_value = offender.value.name
|
|
1206
|
+
except (AttributeError, KeyError):
|
|
1207
|
+
break
|
|
1208
|
+
orig_offender = offender
|
|
1209
|
+
if offender_value.startswith("$"):
|
|
1210
|
+
offender = find_offender(
|
|
1211
|
+
offender_value, exhaustive=exhaustive
|
|
1212
|
+
)
|
|
1213
|
+
if offender is None:
|
|
1214
|
+
offender = orig_offender
|
|
1215
|
+
break
|
|
1216
|
+
return offender
|
|
1217
|
+
|
|
1218
|
+
def diagnose_imprecision(offender):
|
|
1219
|
+
# helper for diagnosing imprecise types
|
|
1220
|
+
|
|
1221
|
+
list_msg = """\n
|
|
1222
|
+
For Numba to be able to compile a list, the list must have a known and
|
|
1223
|
+
precise type that can be inferred from the other variables. Whilst sometimes
|
|
1224
|
+
the type of empty lists can be inferred, this is not always the case, see this
|
|
1225
|
+
documentation for help:
|
|
1226
|
+
|
|
1227
|
+
https://numba.readthedocs.io/en/stable/user/troubleshoot.html#my-code-has-an-untyped-list-problem
|
|
1228
|
+
"""
|
|
1229
|
+
if offender is not None:
|
|
1230
|
+
# This block deals with imprecise lists
|
|
1231
|
+
if hasattr(offender, "value"):
|
|
1232
|
+
if hasattr(offender.value, "op"):
|
|
1233
|
+
# might be `foo = []`
|
|
1234
|
+
if offender.value.op == "build_list":
|
|
1235
|
+
return list_msg
|
|
1236
|
+
# or might be `foo = list()`
|
|
1237
|
+
elif offender.value.op == "call":
|
|
1238
|
+
try: # assignment involving a call
|
|
1239
|
+
call_name = offender.value.func.name
|
|
1240
|
+
# find the offender based on the call name
|
|
1241
|
+
offender = find_offender(call_name)
|
|
1242
|
+
if isinstance(offender.value, ir.Global):
|
|
1243
|
+
if offender.value.name == "list":
|
|
1244
|
+
return list_msg
|
|
1245
|
+
except (AttributeError, KeyError):
|
|
1246
|
+
pass
|
|
1247
|
+
return "" # no help possible
|
|
1248
|
+
|
|
1249
|
+
def check_var(name):
|
|
1250
|
+
tv = self.typevars[name]
|
|
1251
|
+
if not tv.defined:
|
|
1252
|
+
if raise_errors:
|
|
1253
|
+
offender = find_offender(name)
|
|
1254
|
+
val = getattr(offender, "value", "unknown operation")
|
|
1255
|
+
loc = getattr(offender, "loc", ir.unknown_loc)
|
|
1256
|
+
msg = (
|
|
1257
|
+
"Type of variable '%s' cannot be determined, "
|
|
1258
|
+
"operation: %s, location: %s"
|
|
1259
|
+
)
|
|
1260
|
+
raise TypingError(msg % (var, val, loc), loc)
|
|
1261
|
+
else:
|
|
1262
|
+
typdict[var] = types.unknown
|
|
1263
|
+
return
|
|
1264
|
+
tp = tv.getone()
|
|
1265
|
+
|
|
1266
|
+
if isinstance(tp, types.UndefinedFunctionType):
|
|
1267
|
+
tp = tp.get_precise()
|
|
1268
|
+
|
|
1269
|
+
if not tp.is_precise():
|
|
1270
|
+
offender = find_offender(name, exhaustive=True)
|
|
1271
|
+
msg = (
|
|
1272
|
+
"Cannot infer the type of variable '%s'%s, "
|
|
1273
|
+
"have imprecise type: %s. %s"
|
|
1274
|
+
)
|
|
1275
|
+
istmp = " (temporary variable)" if var.startswith("$") else ""
|
|
1276
|
+
loc = getattr(offender, "loc", ir.unknown_loc)
|
|
1277
|
+
# is this an untyped list? try and provide help
|
|
1278
|
+
extra_msg = diagnose_imprecision(offender)
|
|
1279
|
+
if raise_errors:
|
|
1280
|
+
raise TypingError(msg % (var, istmp, tp, extra_msg), loc)
|
|
1281
|
+
else:
|
|
1282
|
+
typdict[var] = types.unknown
|
|
1283
|
+
return
|
|
1284
|
+
else: # type is precise, hold it
|
|
1285
|
+
typdict[var] = tp
|
|
1286
|
+
|
|
1287
|
+
# For better error display, check first user-visible vars, then
|
|
1288
|
+
# temporaries
|
|
1289
|
+
temps = set(k for k in self.typevars if not k[0].isalpha())
|
|
1290
|
+
others = set(self.typevars) - temps
|
|
1291
|
+
for var in sorted(others):
|
|
1292
|
+
check_var(var)
|
|
1293
|
+
for var in sorted(temps):
|
|
1294
|
+
check_var(var)
|
|
1295
|
+
|
|
1296
|
+
try:
|
|
1297
|
+
retty = self.get_return_type(typdict)
|
|
1298
|
+
except Exception as e:
|
|
1299
|
+
# partial type inference may raise e.g. attribute error if a
|
|
1300
|
+
# constraint has no computable signature, ignore this as needed
|
|
1301
|
+
if raise_errors:
|
|
1302
|
+
raise e
|
|
1303
|
+
else:
|
|
1304
|
+
retty = None
|
|
1305
|
+
else:
|
|
1306
|
+
typdict = utils.UniqueDict(
|
|
1307
|
+
typdict, **{v.name: retty for v in self._get_return_vars()}
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
try:
|
|
1311
|
+
fntys = self.get_function_types(typdict)
|
|
1312
|
+
except Exception as e:
|
|
1313
|
+
# partial type inference may raise e.g. attribute error if a
|
|
1314
|
+
# constraint has no computable signature, ignore this as needed
|
|
1315
|
+
if raise_errors:
|
|
1316
|
+
raise e
|
|
1317
|
+
else:
|
|
1318
|
+
fntys = None
|
|
1319
|
+
|
|
1320
|
+
if self.generator_info:
|
|
1321
|
+
retty = self.get_generator_type(
|
|
1322
|
+
typdict, retty, raise_errors=raise_errors
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
def check_undef_var_in_calls():
|
|
1326
|
+
# Check for undefined variables in the call arguments.
|
|
1327
|
+
for callnode, calltype in self.calltypes.items():
|
|
1328
|
+
if calltype is not None:
|
|
1329
|
+
for i, v in enumerate(calltype.args, start=1):
|
|
1330
|
+
if v is types._undef_var:
|
|
1331
|
+
m = f"undefined variable used in call argument #{i}"
|
|
1332
|
+
raise TypingError(m, loc=callnode.loc)
|
|
1333
|
+
|
|
1334
|
+
check_undef_var_in_calls()
|
|
1335
|
+
|
|
1336
|
+
self.debug.unify_finished(typdict, retty, fntys)
|
|
1337
|
+
|
|
1338
|
+
return typdict, retty, fntys
|
|
1339
|
+
|
|
1340
|
+
def get_generator_type(self, typdict, retty, raise_errors=True):
|
|
1341
|
+
gi = self.generator_info
|
|
1342
|
+
arg_types = [None] * len(self.arg_names)
|
|
1343
|
+
for index, name in self.arg_names.items():
|
|
1344
|
+
arg_types[index] = typdict[name]
|
|
1345
|
+
|
|
1346
|
+
state_types = None
|
|
1347
|
+
try:
|
|
1348
|
+
state_types = [typdict[var_name] for var_name in gi.state_vars]
|
|
1349
|
+
except KeyError:
|
|
1350
|
+
msg = "Cannot type generator: state variable types cannot be found"
|
|
1351
|
+
if raise_errors:
|
|
1352
|
+
raise TypingError(msg)
|
|
1353
|
+
state_types = [types.unknown for _ in gi.state_vars]
|
|
1354
|
+
|
|
1355
|
+
yield_types = None
|
|
1356
|
+
try:
|
|
1357
|
+
yield_types = [
|
|
1358
|
+
typdict[y.inst.value.name] for y in gi.get_yield_points()
|
|
1359
|
+
]
|
|
1360
|
+
except KeyError:
|
|
1361
|
+
msg = "Cannot type generator: yield type cannot be found"
|
|
1362
|
+
if raise_errors:
|
|
1363
|
+
raise TypingError(msg)
|
|
1364
|
+
if not yield_types:
|
|
1365
|
+
msg = "Cannot type generator: it does not yield any value"
|
|
1366
|
+
if raise_errors:
|
|
1367
|
+
raise TypingError(msg)
|
|
1368
|
+
yield_types = [types.unknown for _ in gi.get_yield_points()]
|
|
1369
|
+
|
|
1370
|
+
if not yield_types or all(yield_types) == types.unknown:
|
|
1371
|
+
# unknown yield, probably partial type inference, escape
|
|
1372
|
+
return types.Generator(
|
|
1373
|
+
self.func_id.func,
|
|
1374
|
+
types.unknown,
|
|
1375
|
+
arg_types,
|
|
1376
|
+
state_types,
|
|
1377
|
+
has_finalizer=True,
|
|
1378
|
+
)
|
|
1379
|
+
|
|
1380
|
+
yield_type = self.context.unify_types(*yield_types)
|
|
1381
|
+
if yield_type is None or isinstance(yield_type, types.Optional):
|
|
1382
|
+
msg = "Cannot type generator: cannot unify yielded types %s"
|
|
1383
|
+
yp_highlights = []
|
|
1384
|
+
for y in gi.get_yield_points():
|
|
1385
|
+
msg = _termcolor.errmsg(
|
|
1386
|
+
"Yield of: IR '%s', type '%s', location: %s"
|
|
1387
|
+
)
|
|
1388
|
+
yp_highlights.append(
|
|
1389
|
+
msg
|
|
1390
|
+
% (
|
|
1391
|
+
str(y.inst),
|
|
1392
|
+
typdict[y.inst.value.name],
|
|
1393
|
+
y.inst.loc.strformat(),
|
|
1394
|
+
)
|
|
1395
|
+
)
|
|
1396
|
+
|
|
1397
|
+
explain_ty = set()
|
|
1398
|
+
for ty in yield_types:
|
|
1399
|
+
if isinstance(ty, types.Optional):
|
|
1400
|
+
explain_ty.add(ty.type)
|
|
1401
|
+
explain_ty.add(types.NoneType("none"))
|
|
1402
|
+
else:
|
|
1403
|
+
explain_ty.add(ty)
|
|
1404
|
+
if raise_errors:
|
|
1405
|
+
raise TypingError(
|
|
1406
|
+
"Can't unify yield type from the "
|
|
1407
|
+
"following types: %s"
|
|
1408
|
+
% ", ".join(sorted(map(str, explain_ty)))
|
|
1409
|
+
+ "\n\n"
|
|
1410
|
+
+ "\n".join(yp_highlights)
|
|
1411
|
+
)
|
|
1412
|
+
|
|
1413
|
+
return types.Generator(
|
|
1414
|
+
self.func_id.func,
|
|
1415
|
+
yield_type,
|
|
1416
|
+
arg_types,
|
|
1417
|
+
state_types,
|
|
1418
|
+
has_finalizer=True,
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
def get_function_types(self, typemap):
|
|
1422
|
+
"""
|
|
1423
|
+
Fill and return the calltypes map.
|
|
1424
|
+
"""
|
|
1425
|
+
# XXX why can't this be done on the fly?
|
|
1426
|
+
calltypes = self.calltypes
|
|
1427
|
+
for call, constraint in self.calls:
|
|
1428
|
+
calltypes[call] = constraint.get_call_signature()
|
|
1429
|
+
return calltypes
|
|
1430
|
+
|
|
1431
|
+
def _unify_return_types(self, rettypes):
|
|
1432
|
+
if rettypes:
|
|
1433
|
+
unified = self.context.unify_types(*rettypes)
|
|
1434
|
+
if isinstance(unified, types.FunctionType):
|
|
1435
|
+
# unified is allowed to be UndefinedFunctionType
|
|
1436
|
+
# instance (that is imprecise).
|
|
1437
|
+
return unified
|
|
1438
|
+
if unified is None or not unified.is_precise():
|
|
1439
|
+
|
|
1440
|
+
def check_type(atype):
|
|
1441
|
+
lst = []
|
|
1442
|
+
for k, v in self.typevars.items():
|
|
1443
|
+
if atype == v.type:
|
|
1444
|
+
lst.append(k)
|
|
1445
|
+
returns = {}
|
|
1446
|
+
for x in reversed(lst):
|
|
1447
|
+
for block in self.func_ir.blocks.values():
|
|
1448
|
+
for instr in block.find_insts(ir.Return):
|
|
1449
|
+
value = instr.value
|
|
1450
|
+
if isinstance(value, ir.Var):
|
|
1451
|
+
name = value.name
|
|
1452
|
+
else:
|
|
1453
|
+
pass
|
|
1454
|
+
if x == name:
|
|
1455
|
+
returns[x] = instr
|
|
1456
|
+
break
|
|
1457
|
+
|
|
1458
|
+
interped = ""
|
|
1459
|
+
for name, offender in returns.items():
|
|
1460
|
+
loc = getattr(offender, "loc", ir.unknown_loc)
|
|
1461
|
+
msg = "Return of: IR name '%s', type '%s', location: %s"
|
|
1462
|
+
interped = msg % (name, atype, loc.strformat())
|
|
1463
|
+
return interped
|
|
1464
|
+
|
|
1465
|
+
problem_str = []
|
|
1466
|
+
for xtype in rettypes:
|
|
1467
|
+
problem_str.append(_termcolor.errmsg(check_type(xtype)))
|
|
1468
|
+
|
|
1469
|
+
raise TypingError(
|
|
1470
|
+
"Can't unify return type from the "
|
|
1471
|
+
"following types: %s"
|
|
1472
|
+
% ", ".join(sorted(map(str, rettypes)))
|
|
1473
|
+
+ "\n"
|
|
1474
|
+
+ "\n".join(problem_str)
|
|
1475
|
+
)
|
|
1476
|
+
return unified
|
|
1477
|
+
else:
|
|
1478
|
+
# Function without a successful return path
|
|
1479
|
+
return types.none
|
|
1480
|
+
|
|
1481
|
+
def get_return_type(self, typemap):
|
|
1482
|
+
rettypes = set()
|
|
1483
|
+
for var in self._get_return_vars():
|
|
1484
|
+
rettypes.add(typemap[var.name])
|
|
1485
|
+
retty = self._unify_return_types(rettypes)
|
|
1486
|
+
# Check return value is not undefined
|
|
1487
|
+
if retty is types._undef_var:
|
|
1488
|
+
raise TypingError("return value is undefined")
|
|
1489
|
+
return retty
|
|
1490
|
+
|
|
1491
|
+
def get_state_token(self):
|
|
1492
|
+
"""The algorithm is monotonic. It can only grow or "refine" the
|
|
1493
|
+
typevar map.
|
|
1494
|
+
"""
|
|
1495
|
+
return [tv.type for name, tv in sorted(self.typevars.items())]
|
|
1496
|
+
|
|
1497
|
+
def constrain_statement(self, inst):
|
|
1498
|
+
if isinstance(inst, ir.Assign):
|
|
1499
|
+
self.typeof_assign(inst)
|
|
1500
|
+
elif isinstance(inst, ir.SetItem):
|
|
1501
|
+
self.typeof_setitem(inst)
|
|
1502
|
+
elif isinstance(inst, ir.StaticSetItem):
|
|
1503
|
+
self.typeof_static_setitem(inst)
|
|
1504
|
+
elif isinstance(inst, ir.DelItem):
|
|
1505
|
+
self.typeof_delitem(inst)
|
|
1506
|
+
elif isinstance(inst, ir.SetAttr):
|
|
1507
|
+
self.typeof_setattr(inst)
|
|
1508
|
+
elif isinstance(inst, ir.Print):
|
|
1509
|
+
self.typeof_print(inst)
|
|
1510
|
+
elif isinstance(inst, ir.StoreMap):
|
|
1511
|
+
self.typeof_storemap(inst)
|
|
1512
|
+
elif isinstance(inst, (ir.Jump, ir.Branch, ir.Return, ir.Del)):
|
|
1513
|
+
pass
|
|
1514
|
+
elif isinstance(inst, (ir.DynamicRaise, ir.DynamicTryRaise)):
|
|
1515
|
+
pass
|
|
1516
|
+
elif isinstance(inst, (ir.StaticRaise, ir.StaticTryRaise)):
|
|
1517
|
+
pass
|
|
1518
|
+
elif isinstance(inst, ir.PopBlock):
|
|
1519
|
+
pass # It's a marker statement
|
|
1520
|
+
elif type(inst) in typeinfer_extensions:
|
|
1521
|
+
# let external calls handle stmt if type matches
|
|
1522
|
+
f = typeinfer_extensions[type(inst)]
|
|
1523
|
+
f(inst, self)
|
|
1524
|
+
else:
|
|
1525
|
+
msg = "Unsupported constraint encountered: %s" % inst
|
|
1526
|
+
raise UnsupportedError(msg, loc=inst.loc)
|
|
1527
|
+
|
|
1528
|
+
def typeof_setitem(self, inst):
|
|
1529
|
+
constraint = SetItemConstraint(
|
|
1530
|
+
target=inst.target, index=inst.index, value=inst.value, loc=inst.loc
|
|
1531
|
+
)
|
|
1532
|
+
self.constraints.append(constraint)
|
|
1533
|
+
self.calls.append((inst, constraint))
|
|
1534
|
+
|
|
1535
|
+
def typeof_storemap(self, inst):
|
|
1536
|
+
constraint = SetItemConstraint(
|
|
1537
|
+
target=inst.dct, index=inst.key, value=inst.value, loc=inst.loc
|
|
1538
|
+
)
|
|
1539
|
+
self.constraints.append(constraint)
|
|
1540
|
+
self.calls.append((inst, constraint))
|
|
1541
|
+
|
|
1542
|
+
def typeof_static_setitem(self, inst):
|
|
1543
|
+
constraint = StaticSetItemConstraint(
|
|
1544
|
+
target=inst.target,
|
|
1545
|
+
index=inst.index,
|
|
1546
|
+
index_var=inst.index_var,
|
|
1547
|
+
value=inst.value,
|
|
1548
|
+
loc=inst.loc,
|
|
1549
|
+
)
|
|
1550
|
+
self.constraints.append(constraint)
|
|
1551
|
+
self.calls.append((inst, constraint))
|
|
1552
|
+
|
|
1553
|
+
def typeof_delitem(self, inst):
|
|
1554
|
+
constraint = DelItemConstraint(
|
|
1555
|
+
target=inst.target, index=inst.index, loc=inst.loc
|
|
1556
|
+
)
|
|
1557
|
+
self.constraints.append(constraint)
|
|
1558
|
+
self.calls.append((inst, constraint))
|
|
1559
|
+
|
|
1560
|
+
def typeof_setattr(self, inst):
|
|
1561
|
+
constraint = SetAttrConstraint(
|
|
1562
|
+
target=inst.target, attr=inst.attr, value=inst.value, loc=inst.loc
|
|
1563
|
+
)
|
|
1564
|
+
self.constraints.append(constraint)
|
|
1565
|
+
self.calls.append((inst, constraint))
|
|
1566
|
+
|
|
1567
|
+
def typeof_print(self, inst):
|
|
1568
|
+
constraint = PrintConstraint(
|
|
1569
|
+
args=inst.args, vararg=inst.vararg, loc=inst.loc
|
|
1570
|
+
)
|
|
1571
|
+
self.constraints.append(constraint)
|
|
1572
|
+
self.calls.append((inst, constraint))
|
|
1573
|
+
|
|
1574
|
+
def typeof_assign(self, inst):
|
|
1575
|
+
value = inst.value
|
|
1576
|
+
if isinstance(value, ir.Const):
|
|
1577
|
+
self.typeof_const(inst, inst.target, value.value)
|
|
1578
|
+
elif isinstance(value, ir.Var):
|
|
1579
|
+
self.constraints.append(
|
|
1580
|
+
Propagate(dst=inst.target.name, src=value.name, loc=inst.loc)
|
|
1581
|
+
)
|
|
1582
|
+
elif isinstance(value, (ir.Global, ir.FreeVar)):
|
|
1583
|
+
self.typeof_global(inst, inst.target, value)
|
|
1584
|
+
elif isinstance(value, ir.Arg):
|
|
1585
|
+
self.typeof_arg(inst, inst.target, value)
|
|
1586
|
+
elif isinstance(value, ir.Expr):
|
|
1587
|
+
self.typeof_expr(inst, inst.target, value)
|
|
1588
|
+
elif isinstance(value, ir.Yield):
|
|
1589
|
+
self.typeof_yield(inst, inst.target, value)
|
|
1590
|
+
else:
|
|
1591
|
+
msg = "Unsupported assignment encountered: %s %s" % (
|
|
1592
|
+
type(value),
|
|
1593
|
+
str(value),
|
|
1594
|
+
)
|
|
1595
|
+
raise UnsupportedError(msg, loc=inst.loc)
|
|
1596
|
+
|
|
1597
|
+
def resolve_value_type(self, inst, val):
|
|
1598
|
+
"""
|
|
1599
|
+
Resolve the type of a simple Python value, such as can be
|
|
1600
|
+
represented by literals.
|
|
1601
|
+
"""
|
|
1602
|
+
try:
|
|
1603
|
+
return self.context.resolve_value_type(val)
|
|
1604
|
+
except ValueError as e:
|
|
1605
|
+
msg = str(e)
|
|
1606
|
+
raise TypingError(msg, loc=inst.loc)
|
|
1607
|
+
|
|
1608
|
+
def typeof_arg(self, inst, target, arg):
|
|
1609
|
+
src_name = self._mangle_arg_name(arg.name)
|
|
1610
|
+
self.constraints.append(
|
|
1611
|
+
ArgConstraint(dst=target.name, src=src_name, loc=inst.loc)
|
|
1612
|
+
)
|
|
1613
|
+
|
|
1614
|
+
def typeof_const(self, inst, target, const):
|
|
1615
|
+
ty = self.resolve_value_type(inst, const)
|
|
1616
|
+
if inst.value.use_literal_type:
|
|
1617
|
+
lit = types.maybe_literal(value=const)
|
|
1618
|
+
else:
|
|
1619
|
+
lit = None
|
|
1620
|
+
self.add_type(target.name, lit or ty, loc=inst.loc)
|
|
1621
|
+
|
|
1622
|
+
def typeof_yield(self, inst, target, yield_):
|
|
1623
|
+
# Sending values into generators isn't supported.
|
|
1624
|
+
self.add_type(target.name, types.none, loc=inst.loc)
|
|
1625
|
+
|
|
1626
|
+
def sentry_modified_builtin(self, inst, gvar):
|
|
1627
|
+
"""
|
|
1628
|
+
Ensure that builtins are not modified.
|
|
1629
|
+
"""
|
|
1630
|
+
if gvar.name == "range" and gvar.value is not range:
|
|
1631
|
+
bad = True
|
|
1632
|
+
elif gvar.name == "slice" and gvar.value is not slice:
|
|
1633
|
+
bad = True
|
|
1634
|
+
elif gvar.name == "len" and gvar.value is not len:
|
|
1635
|
+
bad = True
|
|
1636
|
+
else:
|
|
1637
|
+
bad = False
|
|
1638
|
+
|
|
1639
|
+
if bad:
|
|
1640
|
+
raise TypingError("Modified builtin '%s'" % gvar.name, loc=inst.loc)
|
|
1641
|
+
|
|
1642
|
+
def resolve_call(self, fnty, pos_args, kw_args):
|
|
1643
|
+
"""
|
|
1644
|
+
Resolve a call to a given function type. A signature is returned.
|
|
1645
|
+
"""
|
|
1646
|
+
if isinstance(fnty, types.FunctionType):
|
|
1647
|
+
return fnty.get_call_type(self, pos_args, kw_args)
|
|
1648
|
+
if isinstance(fnty, types.RecursiveCall) and not self._skip_recursion:
|
|
1649
|
+
# Recursive call
|
|
1650
|
+
disp = fnty.dispatcher_type.dispatcher
|
|
1651
|
+
pysig, args = disp.fold_argument_types(pos_args, kw_args)
|
|
1652
|
+
|
|
1653
|
+
frame = self.context.callstack.match(disp.py_func, args)
|
|
1654
|
+
|
|
1655
|
+
# If the signature is not being compiled
|
|
1656
|
+
if frame is None:
|
|
1657
|
+
sig = self.context.resolve_function_type(
|
|
1658
|
+
fnty.dispatcher_type, pos_args, kw_args
|
|
1659
|
+
)
|
|
1660
|
+
fndesc = disp.overloads[args].fndesc
|
|
1661
|
+
qual = qualifying_prefix(fndesc.modname, fndesc.qualname)
|
|
1662
|
+
fnty.add_overloads(args, qual, fndesc.uid)
|
|
1663
|
+
return sig
|
|
1664
|
+
|
|
1665
|
+
fnid = frame.func_id
|
|
1666
|
+
qual = qualifying_prefix(fnid.modname, fnid.func_qualname)
|
|
1667
|
+
fnty.add_overloads(args, qual, fnid.unique_id)
|
|
1668
|
+
# Resume propagation in parent frame
|
|
1669
|
+
return_type = frame.typeinfer.return_types_from_partial()
|
|
1670
|
+
# No known return type
|
|
1671
|
+
if return_type is None:
|
|
1672
|
+
raise TypingError("cannot type infer runaway recursion")
|
|
1673
|
+
|
|
1674
|
+
sig = typing.signature(return_type, *args)
|
|
1675
|
+
sig = sig.replace(pysig=pysig)
|
|
1676
|
+
# Keep track of unique return_type
|
|
1677
|
+
frame.add_return_type(return_type)
|
|
1678
|
+
return sig
|
|
1679
|
+
else:
|
|
1680
|
+
# Normal non-recursive call
|
|
1681
|
+
return self.context.resolve_function_type(fnty, pos_args, kw_args)
|
|
1682
|
+
|
|
1683
|
+
def typeof_global(self, inst, target, gvar):
|
|
1684
|
+
try:
|
|
1685
|
+
typ = self.resolve_value_type(inst, gvar.value)
|
|
1686
|
+
except TypingError as e:
|
|
1687
|
+
if (
|
|
1688
|
+
gvar.name == self.func_id.func_name
|
|
1689
|
+
and gvar.name in _temporary_dispatcher_map
|
|
1690
|
+
):
|
|
1691
|
+
# Self-recursion case where the dispatcher is not (yet?) known
|
|
1692
|
+
# as a global variable
|
|
1693
|
+
typ = types.Dispatcher(_temporary_dispatcher_map[gvar.name])
|
|
1694
|
+
else:
|
|
1695
|
+
from numba.misc import special
|
|
1696
|
+
|
|
1697
|
+
nm = gvar.name
|
|
1698
|
+
# check if the problem is actually a name error
|
|
1699
|
+
func_glbls = self.func_id.func.__globals__
|
|
1700
|
+
if (
|
|
1701
|
+
nm not in func_glbls.keys()
|
|
1702
|
+
and nm not in special.__all__
|
|
1703
|
+
and nm not in __builtins__.keys()
|
|
1704
|
+
and nm not in self.func_id.code.co_freevars
|
|
1705
|
+
):
|
|
1706
|
+
errstr = "NameError: name '%s' is not defined"
|
|
1707
|
+
msg = _termcolor.errmsg(errstr % nm)
|
|
1708
|
+
e.patch_message(msg)
|
|
1709
|
+
raise
|
|
1710
|
+
else:
|
|
1711
|
+
msg = _termcolor.errmsg("Untyped global name '%s':" % nm)
|
|
1712
|
+
msg += " %s" # interps the actual error
|
|
1713
|
+
|
|
1714
|
+
# if the untyped global is a numba internal function then add
|
|
1715
|
+
# to the error message asking if it's been imported.
|
|
1716
|
+
|
|
1717
|
+
if nm in special.__all__:
|
|
1718
|
+
tmp = (
|
|
1719
|
+
"\n'%s' looks like a Numba internal function, has "
|
|
1720
|
+
"it been imported (i.e. 'from numba import %s')?\n"
|
|
1721
|
+
% (nm, nm)
|
|
1722
|
+
)
|
|
1723
|
+
msg += _termcolor.errmsg(tmp)
|
|
1724
|
+
e.patch_message(msg % e)
|
|
1725
|
+
raise
|
|
1726
|
+
|
|
1727
|
+
if isinstance(typ, types.Dispatcher) and typ.dispatcher.is_compiling:
|
|
1728
|
+
# Recursive call
|
|
1729
|
+
callstack = self.context.callstack
|
|
1730
|
+
callframe = callstack.findfirst(typ.dispatcher.py_func)
|
|
1731
|
+
if callframe is not None:
|
|
1732
|
+
typ = types.RecursiveCall(typ)
|
|
1733
|
+
else:
|
|
1734
|
+
raise NotImplementedError(
|
|
1735
|
+
"call to %s: unsupported recursion" % typ.dispatcher
|
|
1736
|
+
)
|
|
1737
|
+
|
|
1738
|
+
if isinstance(typ, types.Array):
|
|
1739
|
+
# Global array in nopython mode is constant
|
|
1740
|
+
typ = typ.copy(readonly=True)
|
|
1741
|
+
|
|
1742
|
+
if isinstance(typ, types.BaseAnonymousTuple):
|
|
1743
|
+
# if it's a tuple of literal types, swap the type for the more
|
|
1744
|
+
# specific literal version
|
|
1745
|
+
literaled = [types.maybe_literal(x) for x in gvar.value]
|
|
1746
|
+
if all(literaled):
|
|
1747
|
+
typ = types.Tuple(literaled)
|
|
1748
|
+
|
|
1749
|
+
# if any of the items in the tuple are arrays, they need to be
|
|
1750
|
+
# typed as readonly, mutating an array in a global container
|
|
1751
|
+
# is not supported (should be compile time constant etc).
|
|
1752
|
+
def mark_array_ro(tup):
|
|
1753
|
+
newtup = []
|
|
1754
|
+
for item in tup.types:
|
|
1755
|
+
if isinstance(item, types.Array):
|
|
1756
|
+
item = item.copy(readonly=True)
|
|
1757
|
+
elif isinstance(item, types.BaseAnonymousTuple):
|
|
1758
|
+
item = mark_array_ro(item)
|
|
1759
|
+
newtup.append(item)
|
|
1760
|
+
return types.BaseTuple.from_types(newtup)
|
|
1761
|
+
|
|
1762
|
+
typ = mark_array_ro(typ)
|
|
1763
|
+
|
|
1764
|
+
self.sentry_modified_builtin(inst, gvar)
|
|
1765
|
+
# Setting literal_value for globals because they are handled
|
|
1766
|
+
# like const value in numba
|
|
1767
|
+
lit = types.maybe_literal(gvar.value)
|
|
1768
|
+
# The user may have provided the type for this variable already.
|
|
1769
|
+
# In this case, call add_type() to make sure the value type is
|
|
1770
|
+
# consistent. See numba.tests.test_array_reductions
|
|
1771
|
+
# TestArrayReductions.test_array_cumsum for examples.
|
|
1772
|
+
# Variable type locked by using the locals dict.
|
|
1773
|
+
tv = self.typevars[target.name]
|
|
1774
|
+
if tv.locked:
|
|
1775
|
+
tv.add_type(lit or typ, loc=inst.loc)
|
|
1776
|
+
else:
|
|
1777
|
+
self.lock_type(target.name, lit or typ, loc=inst.loc)
|
|
1778
|
+
self.assumed_immutables.add(inst)
|
|
1779
|
+
|
|
1780
|
+
def typeof_expr(self, inst, target, expr):
|
|
1781
|
+
if expr.op == "call":
|
|
1782
|
+
self.typeof_call(inst, target, expr)
|
|
1783
|
+
elif expr.op in ("getiter", "iternext"):
|
|
1784
|
+
self.typeof_intrinsic_call(inst, target, expr.op, expr.value)
|
|
1785
|
+
elif expr.op == "exhaust_iter":
|
|
1786
|
+
constraint = ExhaustIterConstraint(
|
|
1787
|
+
target.name, count=expr.count, iterator=expr.value, loc=expr.loc
|
|
1788
|
+
)
|
|
1789
|
+
self.constraints.append(constraint)
|
|
1790
|
+
elif expr.op == "pair_first":
|
|
1791
|
+
constraint = PairFirstConstraint(
|
|
1792
|
+
target.name, pair=expr.value, loc=expr.loc
|
|
1793
|
+
)
|
|
1794
|
+
self.constraints.append(constraint)
|
|
1795
|
+
elif expr.op == "pair_second":
|
|
1796
|
+
constraint = PairSecondConstraint(
|
|
1797
|
+
target.name, pair=expr.value, loc=expr.loc
|
|
1798
|
+
)
|
|
1799
|
+
self.constraints.append(constraint)
|
|
1800
|
+
elif expr.op == "binop":
|
|
1801
|
+
self.typeof_intrinsic_call(
|
|
1802
|
+
inst, target, expr.fn, expr.lhs, expr.rhs
|
|
1803
|
+
)
|
|
1804
|
+
elif expr.op == "inplace_binop":
|
|
1805
|
+
self.typeof_intrinsic_call(
|
|
1806
|
+
inst, target, expr.fn, expr.lhs, expr.rhs
|
|
1807
|
+
)
|
|
1808
|
+
elif expr.op == "unary":
|
|
1809
|
+
self.typeof_intrinsic_call(inst, target, expr.fn, expr.value)
|
|
1810
|
+
elif expr.op == "static_getitem":
|
|
1811
|
+
constraint = StaticGetItemConstraint(
|
|
1812
|
+
target.name,
|
|
1813
|
+
value=expr.value,
|
|
1814
|
+
index=expr.index,
|
|
1815
|
+
index_var=expr.index_var,
|
|
1816
|
+
loc=expr.loc,
|
|
1817
|
+
)
|
|
1818
|
+
self.constraints.append(constraint)
|
|
1819
|
+
self.calls.append((inst.value, constraint))
|
|
1820
|
+
elif expr.op == "getitem":
|
|
1821
|
+
self.typeof_intrinsic_call(
|
|
1822
|
+
inst,
|
|
1823
|
+
target,
|
|
1824
|
+
operator.getitem,
|
|
1825
|
+
expr.value,
|
|
1826
|
+
expr.index,
|
|
1827
|
+
)
|
|
1828
|
+
elif expr.op == "typed_getitem":
|
|
1829
|
+
constraint = TypedGetItemConstraint(
|
|
1830
|
+
target.name,
|
|
1831
|
+
value=expr.value,
|
|
1832
|
+
dtype=expr.dtype,
|
|
1833
|
+
index=expr.index,
|
|
1834
|
+
loc=expr.loc,
|
|
1835
|
+
)
|
|
1836
|
+
self.constraints.append(constraint)
|
|
1837
|
+
self.calls.append((inst.value, constraint))
|
|
1838
|
+
|
|
1839
|
+
elif expr.op == "getattr":
|
|
1840
|
+
constraint = GetAttrConstraint(
|
|
1841
|
+
target.name,
|
|
1842
|
+
attr=expr.attr,
|
|
1843
|
+
value=expr.value,
|
|
1844
|
+
loc=inst.loc,
|
|
1845
|
+
inst=inst,
|
|
1846
|
+
)
|
|
1847
|
+
self.constraints.append(constraint)
|
|
1848
|
+
elif expr.op == "build_tuple":
|
|
1849
|
+
constraint = BuildTupleConstraint(
|
|
1850
|
+
target.name, items=expr.items, loc=inst.loc
|
|
1851
|
+
)
|
|
1852
|
+
self.constraints.append(constraint)
|
|
1853
|
+
elif expr.op == "build_list":
|
|
1854
|
+
constraint = BuildListConstraint(
|
|
1855
|
+
target.name, items=expr.items, loc=inst.loc
|
|
1856
|
+
)
|
|
1857
|
+
self.constraints.append(constraint)
|
|
1858
|
+
elif expr.op == "build_set":
|
|
1859
|
+
constraint = BuildSetConstraint(
|
|
1860
|
+
target.name, items=expr.items, loc=inst.loc
|
|
1861
|
+
)
|
|
1862
|
+
self.constraints.append(constraint)
|
|
1863
|
+
elif expr.op == "build_map":
|
|
1864
|
+
constraint = BuildMapConstraint(
|
|
1865
|
+
target.name,
|
|
1866
|
+
items=expr.items,
|
|
1867
|
+
special_value=expr.literal_value,
|
|
1868
|
+
value_indexes=expr.value_indexes,
|
|
1869
|
+
loc=inst.loc,
|
|
1870
|
+
)
|
|
1871
|
+
self.constraints.append(constraint)
|
|
1872
|
+
elif expr.op == "cast":
|
|
1873
|
+
self.constraints.append(
|
|
1874
|
+
Propagate(dst=target.name, src=expr.value.name, loc=inst.loc)
|
|
1875
|
+
)
|
|
1876
|
+
elif expr.op == "phi":
|
|
1877
|
+
for iv in expr.incoming_values:
|
|
1878
|
+
if iv is not ir.UNDEFINED:
|
|
1879
|
+
self.constraints.append(
|
|
1880
|
+
Propagate(dst=target.name, src=iv.name, loc=inst.loc)
|
|
1881
|
+
)
|
|
1882
|
+
elif expr.op == "make_function":
|
|
1883
|
+
self.lock_type(
|
|
1884
|
+
target.name,
|
|
1885
|
+
types.MakeFunctionLiteral(expr),
|
|
1886
|
+
loc=inst.loc,
|
|
1887
|
+
literal_value=expr,
|
|
1888
|
+
)
|
|
1889
|
+
|
|
1890
|
+
elif expr.op == "undef":
|
|
1891
|
+
self.add_type(target.name, types._undef_var, loc=inst.loc)
|
|
1892
|
+
|
|
1893
|
+
else:
|
|
1894
|
+
msg = "Unsupported op-code encountered: %s" % expr
|
|
1895
|
+
raise UnsupportedError(msg, loc=inst.loc)
|
|
1896
|
+
|
|
1897
|
+
def typeof_call(self, inst, target, call):
|
|
1898
|
+
constraint = CallConstraint(
|
|
1899
|
+
target.name,
|
|
1900
|
+
call.func.name,
|
|
1901
|
+
call.args,
|
|
1902
|
+
call.kws,
|
|
1903
|
+
call.vararg,
|
|
1904
|
+
loc=inst.loc,
|
|
1905
|
+
)
|
|
1906
|
+
self.constraints.append(constraint)
|
|
1907
|
+
self.calls.append((inst.value, constraint))
|
|
1908
|
+
|
|
1909
|
+
def typeof_intrinsic_call(self, inst, target, func, *args):
|
|
1910
|
+
constraint = IntrinsicCallConstraint(
|
|
1911
|
+
target.name, func, args, kws=(), vararg=None, loc=inst.loc
|
|
1912
|
+
)
|
|
1913
|
+
self.constraints.append(constraint)
|
|
1914
|
+
self.calls.append((inst.value, constraint))
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
class NullDebug(object):
|
|
1918
|
+
def propagate_started(self):
|
|
1919
|
+
pass
|
|
1920
|
+
|
|
1921
|
+
def propagate_finished(self):
|
|
1922
|
+
pass
|
|
1923
|
+
|
|
1924
|
+
def unify_finished(self, typdict, retty, fntys):
|
|
1925
|
+
pass
|
|
1926
|
+
|
|
1927
|
+
|
|
1928
|
+
class TypeInferDebug(object):
|
|
1929
|
+
def __init__(self, typeinfer):
|
|
1930
|
+
self.typeinfer = typeinfer
|
|
1931
|
+
|
|
1932
|
+
def _dump_state(self):
|
|
1933
|
+
print("---- type variables ----")
|
|
1934
|
+
pprint([v for k, v in sorted(self.typeinfer.typevars.items())])
|
|
1935
|
+
|
|
1936
|
+
def propagate_started(self):
|
|
1937
|
+
print("propagate".center(80, "-"))
|
|
1938
|
+
|
|
1939
|
+
def propagate_finished(self):
|
|
1940
|
+
self._dump_state()
|
|
1941
|
+
|
|
1942
|
+
def unify_finished(self, typdict, retty, fntys):
|
|
1943
|
+
print("Variable types".center(80, "-"))
|
|
1944
|
+
pprint(typdict)
|
|
1945
|
+
print("Return type".center(80, "-"))
|
|
1946
|
+
pprint(retty)
|
|
1947
|
+
print("Call types".center(80, "-"))
|
|
1948
|
+
pprint(fntys)
|