Skip to content

Commit 4944790

Browse files
grievejiameta-codesync[bot]
authored andcommitted
Recognize re-exported PyTorch Tensor in efficiency lints
Summary: PyTorch exposes its public `torch.Tensor` type through an internal re-export in common stub layouts, so the efficiency lints could see a resolved tensor class while still rejecting it because the defining name was not exactly `torch.Tensor`. This made the opt-in lint group look inert in real projects even though the synthetic tests passed. Treat the canonical resolved implementation target for `torch.Tensor` as a PyTorch tensor while keeping the match narrow enough to avoid assuming every `Tensor` class under the torch package is the same type. The regression tests cover both PyTorch’s internal re-export and a user module that re-exports the public tensor alias, which documents that alias chains should resolve back to the canonical tensor identity. Fixes #4062 Reviewed By: yangdanny97 Differential Revision: D111287027 fbshipit-source-id: 3a44a800b16da7beb0d2a1d31c5042ca4071d3ff
1 parent 50ddd72 commit 4944790

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

pyrefly/lib/alt/expr.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,9 +3099,15 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
30993099
}
31003100

31013101
fn is_pytorch_tensor_type(ty: &Type) -> bool {
3102+
fn is_torch_tensor_class(cls: &ClassType) -> bool {
3103+
let module = cls.class_object().module_name();
3104+
let module = module.as_str();
3105+
cls.name().as_str() == "Tensor" && matches!(module, "torch" | "torch._tensor")
3106+
}
3107+
31023108
match ty {
3103-
Type::ClassType(cls) => cls.has_qname("torch", "Tensor"),
3104-
Type::ShapedArray(shaped_array) => shaped_array.base_class.has_qname("torch", "Tensor"),
3109+
Type::ClassType(cls) => is_torch_tensor_class(cls),
3110+
Type::ShapedArray(shaped_array) => is_torch_tensor_class(&shaped_array.base_class),
31053111
_ => false,
31063112
}
31073113
}

pyrefly/lib/test/pytorch_efficiency_lint.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,41 @@ def zeros(*size: int, device: device | None = None) -> Tensor: ...
9393
e
9494
}
9595

96+
fn reexported_torch_env_with_lint() -> TestEnv {
97+
let mut e = TestEnv::new().enable_pytorch_efficiency_lint_error();
98+
e.add(
99+
"torch._tensor",
100+
r#"
101+
class Tensor:
102+
def item(self) -> int | float: ...
103+
def to(self, device: object) -> "Tensor": ...
104+
def cuda(self) -> "Tensor": ...
105+
"#,
106+
);
107+
e.add(
108+
"torch",
109+
r#"
110+
from torch._tensor import Tensor as Tensor
111+
112+
class device: ...
113+
114+
def zeros(*size: int, device: device | None = None) -> Tensor: ...
115+
"#,
116+
);
117+
e
118+
}
119+
120+
fn user_reexported_torch_env_with_lint() -> TestEnv {
121+
let mut e = reexported_torch_env_with_lint();
122+
e.add(
123+
"mytorch",
124+
r#"
125+
from torch import Tensor as Tensor
126+
"#,
127+
);
128+
e
129+
}
130+
96131
testcase!(
97132
test_tensor_item_call,
98133
env_with_lint(),
@@ -129,6 +164,33 @@ def f(x: torch.Tensor, device: torch.device) -> None:
129164
"#,
130165
);
131166

167+
testcase!(
168+
test_reexported_torch_tensor_lints,
169+
reexported_torch_env_with_lint(),
170+
r#"
171+
import torch
172+
173+
def f(x: torch.Tensor, device: torch.device) -> None:
174+
x.item() # E: `Tensor.item()` causes implicit GPU-to-CPU synchronization
175+
x.cuda() # E: `Tensor.cuda()` hard-codes the target device
176+
print(x) # E: printing a `Tensor` causes implicit GPU-to-CPU synchronization
177+
torch.zeros(2, 3).to(device) # E: `torch.zeros(...).to(device)` creates the tensor on CPU first, then copies it
178+
"#,
179+
);
180+
181+
testcase!(
182+
test_user_reexported_torch_tensor_lints,
183+
user_reexported_torch_env_with_lint(),
184+
r#"
185+
import mytorch
186+
187+
def f(x: mytorch.Tensor) -> None:
188+
x.item() # E: `Tensor.item()` causes implicit GPU-to-CPU synchronization
189+
x.cuda() # E: `Tensor.cuda()` hard-codes the target device
190+
print(x) # E: printing a `Tensor` causes implicit GPU-to-CPU synchronization
191+
"#,
192+
);
193+
132194
testcase!(
133195
test_non_tensor_item_call_ok,
134196
env_with_lint(),

0 commit comments

Comments
 (0)