(self)
| 1367 | ) |
| 1368 | |
| 1369 | def __post_init__(self) -> None: |
| 1370 | for arg, ret in zip(self.arguments.out, self.returns): |
| 1371 | assert arg.annotation == ret.annotation, ( |
| 1372 | "Out arguments must have matching return Tensor; furthermore, " |
| 1373 | "the ith-argument needs to correspond to the ith return" |
| 1374 | ) |
| 1375 | # We also enforce that if you have any mutable, positional args, then they are not returned. |
| 1376 | # This makes it easier to group these functions properly with their functional/out= counterparts. |
| 1377 | for a in self.arguments.post_self_positional_mutable: |
| 1378 | assert not any( |
| 1379 | a.annotation == r.annotation for r in self.returns |
| 1380 | ), f"If you have a schema with mutable positional args, we expect them to not be returned. schema: {str(self)}" |
| 1381 | # Invariant: we expect out arguments to appear as keyword arguments in the schema. |
| 1382 | # This means that all mutable returns should be aliased to a keyword argument |
| 1383 | # (except for "self", which we explicitly don't treat as an out argument because of its use in methods) |
| 1384 | # See Note [is_out_fn] |
| 1385 | out_and_self = list(self.arguments.out) + [ |
| 1386 | arg for arg in self.arguments.flat_positional if arg.name == "self" |
| 1387 | ] |
| 1388 | mutable_returns = [ |
| 1389 | ret |
| 1390 | for ret in self.returns |
| 1391 | if ret.annotation is not None and ret.annotation.is_write |
| 1392 | ] |
| 1393 | immutable_returns = [ |
| 1394 | ret |
| 1395 | for ret in self.returns |
| 1396 | if ret.annotation is None or not ret.annotation.is_write |
| 1397 | ] |
| 1398 | # Some assertions: We don't want any functions with a return type of "-> (Tensor(a!), Tensor)", |
| 1399 | # because: |
| 1400 | # (1) It's more annoying to handle properly |
| 1401 | # (2) It's unnecessary - you can't method-chain on the first (mutated) output because it's part of a tuple. |
| 1402 | # Instead, we expect the (a!) argument to not be returned. |
| 1403 | assert ( |
| 1404 | len(mutable_returns) == 0 or len(immutable_returns) == 0 |
| 1405 | ), f"NativeFunctions must have either only mutable returns, or only immutable returns. Found: {str(self)}" |
| 1406 | for ret in mutable_returns: |
| 1407 | assert any(ret.annotation == arg.annotation for arg in out_and_self), ( |
| 1408 | 'All mutable returns must be aliased either to a keyword argument, or to "self". ' |
| 1409 | "Did you forget to mark an out argument as keyword-only?" |
| 1410 | ) |
| 1411 | if self.arguments.out: |
| 1412 | # out= ops that return their mutable inputs are only really useful for method chaining. |
| 1413 | # And method chaining is only really useful if the thing you're returning is a plain Tensor. |
| 1414 | # So ideally, we'd enforce that out= ops with a single plain mutable tensor should return the tensor, |
| 1415 | # and all other types of out= op schemas should return void. |
| 1416 | # There are a bunch of existing out= ops that return tuples of tensors though, so we're stuck with allowing that. |
| 1417 | if any(a.type != BaseType(BaseTy.Tensor) for a in self.arguments.out): |
| 1418 | assert ( |
| 1419 | len(self.returns) == 0 |
| 1420 | ), "out= ops that accept tensor lists as out arguments " |
| 1421 | "are expected to have no return type (since you can't do method chaining on them)" |
| 1422 | else: |
| 1423 | # mutable keyword arguments whose name has _scratch_ prefix are |
| 1424 | # scratch tensors for memory planning and should not be returned |
| 1425 | assert len( |
| 1426 | [ |
nothing calls this directly
no test coverage detected