Skip to content

Commit c3561b3

Browse files
committed
Throw error at annotation time
1 parent 3966374 commit c3561b3

File tree

4 files changed

+24
-19
lines changed

4 files changed

+24
-19
lines changed

pyrefly/lib/alt/solve.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4028,6 +4028,25 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
40284028
"Type variable bounds and constraints must be concrete".to_owned(),
40294029
);
40304030
}
4031+
if type_form_context == TypeFormContext::TypeArgumentForType {
4032+
if let Some(cls) = match &ty {
4033+
Type::ClassType(cls) | Type::SelfType(cls) => Some(cls.class_object().clone()),
4034+
Type::ClassDef(cls) => Some(cls.clone()),
4035+
_ => None,
4036+
} {
4037+
if self.get_metadata_for_class(&cls).is_new_type() {
4038+
return self.error(
4039+
errors,
4040+
range,
4041+
ErrorInfo::Kind(ErrorKind::InvalidAnnotation),
4042+
format!(
4043+
"NewType `{}` is not a class and cannot be used with `type` or `Type`",
4044+
cls.name()
4045+
),
4046+
);
4047+
}
4048+
}
4049+
}
40314050
ty
40324051
}
40334052

pyrefly/lib/solver/solver.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,8 +1247,6 @@ pub enum SubsetError {
12471247
/// An invariant was violated - used for cases that should be unreachabile when - if there is ever a bug - we
12481248
/// would prefer to not panic and get a text location for reproducing rather than just a crash report.
12491249
InternalError(String),
1250-
/// `typing.NewType` objects are functions, not classes, so they cannot be used with `type` or `type[...]`
1251-
NewTypeIsNotClass(Name),
12521250
/// Protocol class names cannot be assigned to `type[P]` when `P` is a protocol
12531251
TypeOfProtocolNeedsConcreteClass(Name),
12541252
// TODO(rechen): replace this with specific reasons
@@ -1278,9 +1276,6 @@ impl SubsetError {
12781276
SubsetError::TypedDict(err) => Some(err.to_error_msg()),
12791277
SubsetError::OpenTypedDict(err) => Some(err.to_error_msg()),
12801278
SubsetError::InternalError(msg) => Some(format!("Pyrefly internal error: {msg}")),
1281-
SubsetError::NewTypeIsNotClass(name) => Some(format!(
1282-
"NewType `{name}` is not a class and cannot be used with `type` or `type[...]`"
1283-
)),
12841279
SubsetError::TypeOfProtocolNeedsConcreteClass(want) => Some(format!(
12851280
"Only concrete classes may be assigned to `type[{want}]` because `{want}` is a protocol"
12861281
)),

pyrefly/lib/solver/subset.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,16 +1153,6 @@ impl<'a, Ans: LookupAnswer> Subset<'a, Ans> {
11531153
self.type_order.has_superclass(got, want),
11541154
SubsetError::Other,
11551155
),
1156-
// Although the object created by a NewType call behaves like a class for type-checking
1157-
// purposes, it isn't one at runtime, so don't allow it to match `type`.
1158-
(Type::ClassDef(got), Type::Type(_)) if self.type_order.is_new_type(got) => {
1159-
Err(SubsetError::NewTypeIsNotClass(got.name().clone()))
1160-
}
1161-
(Type::ClassDef(got), Type::ClassType(want))
1162-
if self.type_order.is_new_type(got) && want.is_builtin("type") =>
1163-
{
1164-
Err(SubsetError::NewTypeIsNotClass(got.name().clone()))
1165-
}
11661156
(Type::ClassDef(got), Type::Type(box Type::ClassType(want)))
11671157
if self.type_order.is_protocol(want.class_object())
11681158
&& self.type_order.is_protocol(got) =>

pyrefly/lib/test/new_type.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,16 @@ y: type[Any] = Foo # E: `type[Foo]` is not assignable to `type[Any]`
139139
testcase!(
140140
test_new_type_type_argument,
141141
r#"
142-
from typing import NewType
142+
from typing import NewType, Type
143143
144144
Thing = NewType("Thing", int)
145-
ThingType = type[Thing]
145+
ThingType = type[Thing] # E: NewType `Thing` is not a class and cannot be used with `type` or `Type`
146+
OtherThingType = Type[Thing] # E: NewType `Thing` is not a class and cannot be used with `type` or `Type`
146147
147-
mapping: dict[int, ThingType] = {1: Thing} # E: NewType `Thing` is not a class
148+
mapping: dict[int, ThingType] = {1: Thing}
148149
149150
def func(x: ThingType) -> None: ...
150-
func(Thing) # E: NewType `Thing` is not a class
151+
func(Thing)
151152
"#,
152153
);
153154

0 commit comments

Comments
 (0)