Skip to content

Commit 1548298

Browse files
stepanchegfacebook-github-bot
authored andcommitted
type as type
Summary: ``` isinstance(list[str], type) # True isinstance(1, type) # False ``` Python [allows `type` function used as type](https://docs.python.org/3/library/typing.html#the-type-of-class-objects). Antlir uses types in function signatures by (incorrectly) enumerating all possibilities. But this is done in preparation to remove `Ty::name`. Reviewed By: ianlevesque Differential Revision: D48936317 fbshipit-source-id: 28622cbaf348a322095a01b8cc2f5bdf2f6994b6
1 parent 6ffad06 commit 1548298

File tree

12 files changed

+179
-40
lines changed

12 files changed

+179
-40
lines changed

starlark/src/stdlib/funcs/other.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use crate::values::tuple::TupleRef;
4444
use crate::values::types::int_or_big::StarlarkInt;
4545
use crate::values::types::int_or_big::StarlarkIntRef;
4646
use crate::values::typing::never::StarlarkNever;
47+
use crate::values::typing::ty::AbstractType;
4748
use crate::values::typing::StarlarkIter;
4849
use crate::values::value_of_unchecked::ValueOfUnchecked;
4950
use crate::values::AllocValue;
@@ -862,7 +863,7 @@ pub(crate) fn register_other(builder: &mut GlobalsBuilder) {
862863
/// type("hello") == "string"
863864
/// # "#);
864865
/// ```
865-
#[starlark(speculative_exec_safe)]
866+
#[starlark(speculative_exec_safe, as_type = AbstractType)]
866867
fn r#type<'v>(#[starlark(require = pos)] a: Value) -> anyhow::Result<FrozenStringValue> {
867868
Ok(a.get_type_value())
868869
}

starlark/src/tests/docs/rustdocs.rs

+36-18
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,59 @@
1717

1818
use std::collections::HashMap;
1919

20+
use allocative::Allocative;
2021
use starlark_derive::starlark_module;
22+
use starlark_derive::starlark_value;
23+
use starlark_derive::NoSerialize;
24+
use starlark_derive::ProvidesStaticType;
2125
use starlark_map::small_map::SmallMap;
2226

2327
use crate as starlark;
24-
use crate::assert;
28+
use crate::assert::Assert;
2529
use crate::docs::DocItem;
2630
use crate::docs::DocMember;
2731
use crate::environment::GlobalsBuilder;
2832
use crate::eval::Arguments;
2933
use crate::eval::Evaluator;
30-
use crate::typing::Ty;
3134
use crate::values::none::NoneType;
32-
use crate::values::type_repr::StarlarkTypeRepr;
35+
use crate::values::starlark_value_as_type::StarlarkValueAsType;
3336
use crate::values::Heap;
37+
use crate::values::StarlarkValue;
3438
use crate::values::StringValue;
3539
use crate::values::Value;
3640
use crate::values::ValueOfUnchecked;
3741

42+
#[derive(
43+
Debug,
44+
derive_more::Display,
45+
Allocative,
46+
NoSerialize,
47+
ProvidesStaticType
48+
)]
49+
#[display(fmt = "input")]
3850
struct InputTypeRepr;
51+
#[derive(
52+
Debug,
53+
derive_more::Display,
54+
Allocative,
55+
NoSerialize,
56+
ProvidesStaticType
57+
)]
58+
#[display(fmt = "output")]
3959
struct OutputTypeRepr;
4060

41-
impl StarlarkTypeRepr for InputTypeRepr {
42-
fn starlark_type_repr() -> Ty {
43-
Ty::name_static("input")
44-
}
45-
}
46-
impl StarlarkTypeRepr for OutputTypeRepr {
47-
fn starlark_type_repr() -> Ty {
48-
Ty::name_static("output")
49-
}
50-
}
61+
#[starlark_value(type = "input")]
62+
impl<'v> StarlarkValue<'v> for InputTypeRepr {}
63+
64+
#[starlark_value(type = "output")]
65+
impl<'v> StarlarkValue<'v> for OutputTypeRepr {}
5166

5267
#[starlark_module]
5368
#[allow(unused_variables)] // Since this is for a test
5469
fn globals(builder: &mut GlobalsBuilder) {
70+
const Input: StarlarkValueAsType<InputTypeRepr> = StarlarkValueAsType::new();
71+
const Output: StarlarkValueAsType<OutputTypeRepr> = StarlarkValueAsType::new();
72+
5573
fn simple(
5674
arg_int: i32,
5775
arg_bool: bool,
@@ -97,18 +115,18 @@ fn globals(builder: &mut GlobalsBuilder) {
97115
#[test]
98116
fn test_rustdoc() {
99117
let got = GlobalsBuilder::new().with(globals).build();
100-
let expected = assert::pass_module(
101-
r#"
118+
let mut a = Assert::new();
119+
a.globals_add(globals);
120+
let expected = a.pass_module(r#"
102121
# @starlark-rust: allow_string_literals_in_type_expr
103122
104123
def args_kwargs(*args, **kwargs: typing.Any) -> None: pass
105-
def custom_types(arg1: str, arg2: "input") -> "output": pass
124+
def custom_types(arg1: str, arg2: Input) -> Output: pass
106125
def default_arg(arg1 = "_", arg2: typing.Any = None) -> list[str]: pass
107126
def pos_named(arg1: int, *, arg2: int) -> int: pass
108127
def simple(arg_int: int, arg_bool: bool, arg_vec: list[str], arg_dict: dict[str, (bool, int)]) -> None: pass
109128
def with_arguments(*args, **kwargs) -> int: pass
110-
"#,
111-
);
129+
"#);
112130

113131
fn unpack(x: DocItem) -> HashMap<String, DocItem> {
114132
match x {

starlark/src/typing/basic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ pub enum TyBasic {
5050
Iter(ArcTy),
5151
/// `typing.Callable`.
5252
Callable,
53+
/// `type`.
54+
Type,
5355
/// A list.
5456
List(ArcTy),
5557
/// A tuple. May be empty, to indicate the empty tuple.
@@ -120,6 +122,7 @@ impl TyBasic {
120122
TyBasic::List(_) => Some("list"),
121123
TyBasic::Tuple(_) => Some("tuple"),
122124
TyBasic::Dict(..) => Some("dict"),
125+
TyBasic::Type => Some("type"),
123126
TyBasic::Custom(c) => c.as_name(),
124127
TyBasic::Any | TyBasic::Iter(_) | TyBasic::Callable => None,
125128
}
@@ -169,6 +172,7 @@ impl Display for TyBasic {
169172
TyBasic::List(x) => write!(f, "list[{}]", x),
170173
TyBasic::Tuple(tuple) => Display::fmt(tuple, f),
171174
TyBasic::Dict(k, v) => write!(f, "dict[{}, {}]", k, v),
175+
TyBasic::Type => write!(f, "type"),
172176
TyBasic::Custom(c) => Display::fmt(c, f),
173177
}
174178
}

starlark/src/typing/oracle/ctx.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ impl<'a> TypingOracleCtx<'a> {
296296
ty: fun.to_string(),
297297
},
298298
)),
299-
TyBasic::Iter(_) => {
299+
TyBasic::Iter(_) | TyBasic::Type => {
300300
// Unknown type, may be callable.
301301
Ok(Ty::any())
302302
}
@@ -349,6 +349,7 @@ impl<'a> TypingOracleCtx<'a> {
349349
TyBasic::Dict(k, _v) => Ok((**k).dupe()),
350350
TyBasic::Tuple(tuple) => Ok(tuple.item_ty()),
351351
TyBasic::Callable => Ok(Ty::any()),
352+
TyBasic::Type => Ok(Ty::any()),
352353
TyBasic::Iter(ty) => Ok(ty.to_ty()),
353354
TyBasic::Custom(ty) => ty.0.iter_item_dyn(),
354355
TyBasic::Name(_) => Ok(Ty::any()),
@@ -374,7 +375,9 @@ impl<'a> TypingOracleCtx<'a> {
374375
index: Spanned<&TyBasic>,
375376
) -> Result<Result<Ty, ()>, InternalError> {
376377
match array {
377-
TyBasic::Any | TyBasic::Callable | TyBasic::Iter(_) => Ok(Ok(Ty::any())),
378+
TyBasic::Any | TyBasic::Callable | TyBasic::Iter(_) | TyBasic::Type => {
379+
Ok(Ok(Ty::any()))
380+
}
378381
TyBasic::Tuple(tuple) => {
379382
if !self.intersects_basic(index.node, &TyBasic::int()) {
380383
return Ok(Err(()));
@@ -467,7 +470,7 @@ impl<'a> TypingOracleCtx<'a> {
467470

468471
fn expr_dot_basic(&self, array: &TyBasic, attr: &str) -> Result<Ty, ()> {
469472
match array {
470-
TyBasic::Any | TyBasic::Callable | TyBasic::Iter(_) => Ok(Ty::any()),
473+
TyBasic::Any | TyBasic::Callable | TyBasic::Iter(_) | TyBasic::Type => Ok(Ty::any()),
471474
TyBasic::StarlarkValue(s) => s.attr(attr),
472475
TyBasic::Tuple(_) => Err(()),
473476
TyBasic::List(elem) => match attr {
@@ -567,7 +570,7 @@ impl<'a> TypingOracleCtx<'a> {
567570
rhs: Spanned<&TyBasic>,
568571
) -> Result<Ty, ()> {
569572
match lhs {
570-
TyBasic::Any | TyBasic::Iter(_) | TyBasic::Callable => Ok(Ty::any()),
573+
TyBasic::Any | TyBasic::Iter(_) | TyBasic::Callable | TyBasic::Type => Ok(Ty::any()),
571574
TyBasic::StarlarkValue(lhs) => lhs.bin_op(bin_op, rhs.node),
572575
lhs @ TyBasic::List(elem) => match bin_op {
573576
TypingBinOp::Less => {
@@ -846,6 +849,11 @@ impl<'a> TypingOracleCtx<'a> {
846849
Err(()) => false,
847850
},
848851
(TyBasic::Custom(x), y) => x.intersects_with(y),
852+
(TyBasic::Type, TyBasic::StarlarkValue(y)) => y.is_type(),
853+
(TyBasic::Type, _) => {
854+
// TODO(nga): more precise.
855+
true
856+
}
849857
(x, y) if x.is_function() && y.is_function() => true,
850858
// There are lots of other cases that overlap, but add them as we need them
851859
_ => false,

starlark/src/typing/starlark_value.rs

+11
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,17 @@ impl TyStarlarkValue {
273273
vtable.HAS_iterate || vtable.HAS_iterate_collect
274274
}
275275

276+
/// Instance of this type can be evaluated as a type.
277+
#[inline]
278+
pub(crate) fn is_type_from_vtable(vtable: &StarlarkValueVTable) -> bool {
279+
vtable.HAS_eval_type
280+
}
281+
282+
pub(crate) fn is_type(self) -> bool {
283+
self.self_check();
284+
Self::is_type_from_vtable(&self.vtable.vtable)
285+
}
286+
276287
pub(crate) fn iter_item(self) -> Result<Ty, ()> {
277288
if Self::is_iterable(&self.vtable.vtable) {
278289
Ok(Ty::any())

starlark/src/typing/ty.rs

-13
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,6 @@ impl TyName {
135135
TyName(s.into())
136136
}
137137

138-
pub(crate) fn new_static(s: &'static str) -> TyName {
139-
TyName(ArcStr::new_static(s))
140-
}
141-
142138
/// Get the underlying `str` for a `TyName`.
143139
pub fn as_str(&self) -> &str {
144140
&self.0
@@ -190,15 +186,6 @@ impl Ty {
190186
}
191187
}
192188

193-
/// Create a [`Ty::Name`], or one of the standard functions.
194-
pub(crate) fn name_static(name: &'static str) -> Self {
195-
if let Some(x) = Self::try_name_special(name) {
196-
x
197-
} else {
198-
Ty::basic(TyBasic::Name(TyName::new_static(name)))
199-
}
200-
}
201-
202189
pub(crate) fn name(name: &str) -> Self {
203190
if let Some(x) = Self::try_name_special(name) {
204191
x

starlark/src/values/types/starlark_value_as_type.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use crate::values::layout::avalue::AValueImpl;
3535
use crate::values::layout::avalue::Basic;
3636
use crate::values::layout::heap::repr::AValueRepr;
3737
use crate::values::type_repr::StarlarkTypeRepr;
38+
use crate::values::typing::ty::AbstractType;
3839
use crate::values::AllocFrozenValue;
3940
use crate::values::FrozenHeap;
4041
use crate::values::FrozenValue;
@@ -45,6 +46,8 @@ struct StarlarkValueAsTypeStarlarkValue(fn() -> Ty);
4546

4647
#[starlark_value(type = "type")]
4748
impl<'v> StarlarkValue<'v> for StarlarkValueAsTypeStarlarkValue {
49+
type Canonical = AbstractType;
50+
4851
fn eval_type(&self) -> Option<Ty> {
4952
Some((self.0)())
5053
}
@@ -118,8 +121,7 @@ impl<T: StarlarkTypeRepr> Default for StarlarkValueAsType<T> {
118121

119122
impl<T: StarlarkTypeRepr> StarlarkTypeRepr for StarlarkValueAsType<T> {
120123
fn starlark_type_repr() -> Ty {
121-
// TODO(nga): make it proper type.
122-
Ty::name_static("type")
124+
AbstractType::starlark_type_repr()
123125
}
124126
}
125127

starlark/src/values/typing/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub(crate) mod globals;
2323
pub(crate) mod iter;
2424
pub mod macro_refs;
2525
pub(crate) mod never;
26+
pub(crate) mod ty;
2627
pub(crate) mod type_compiled;
2728

2829
pub use crate::values::types::type_instance_id::TypeInstanceId;

starlark/src/values/typing/ty.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2019 The Starlark in Rust Authors.
3+
* Copyright (c) Facebook, Inc. and its affiliates.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
use allocative::Allocative;
19+
use starlark_derive::starlark_value;
20+
use starlark_derive::NoSerialize;
21+
use starlark_derive::ProvidesStaticType;
22+
23+
use crate as starlark;
24+
use crate::typing::Ty;
25+
use crate::typing::TyBasic;
26+
use crate::values::StarlarkValue;
27+
28+
/// Type of type.
29+
#[derive(
30+
Debug,
31+
derive_more::Display,
32+
Allocative,
33+
ProvidesStaticType,
34+
NoSerialize
35+
)]
36+
#[display(fmt = "type")]
37+
#[allocative(skip)] // TODO(nga): derive.
38+
pub enum AbstractType {}
39+
40+
#[starlark_value(type = "type")]
41+
impl<'v> StarlarkValue<'v> for AbstractType {
42+
fn get_type_starlark_repr() -> Ty {
43+
Ty::basic(TyBasic::Type)
44+
}
45+
46+
fn eval_type(&self) -> Option<Ty> {
47+
unreachable!(
48+
"This is unreachable, but this function is needed \
49+
so `TyStarlarkValue` could think this is a type"
50+
)
51+
}
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use crate::assert;
57+
58+
#[test]
59+
fn test_isinstance() {
60+
assert::is_true("isinstance(int, type)");
61+
assert::is_false("isinstance(1, type)");
62+
assert::is_true("isinstance(list[str], type)");
63+
assert::is_true("isinstance(eval_type(list), type)");
64+
}
65+
66+
#[test]
67+
fn test_pass() {
68+
assert::pass(
69+
r#"
70+
def accepts_type(t: type):
71+
pass
72+
73+
def test():
74+
accepts_type(int)
75+
accepts_type(list[str])
76+
accepts_type(None | int)
77+
78+
test()
79+
"#,
80+
);
81+
}
82+
83+
#[test]
84+
fn test_fail_compile_time() {
85+
assert::fail(
86+
r#"
87+
def accepts_type(t: type):
88+
pass
89+
90+
def test():
91+
accepts_type(1)
92+
"#,
93+
"Expected type `type` but got `int`",
94+
);
95+
}
96+
}

starlark/src/values/typing/type_compiled/alloc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use crate::values::typing::type_compiled::matchers::IsName;
3939
use crate::values::typing::type_compiled::matchers::IsNever;
4040
use crate::values::typing::type_compiled::matchers::IsNone;
4141
use crate::values::typing::type_compiled::matchers::IsStr;
42+
use crate::values::typing::type_compiled::matchers::IsType;
4243
use crate::values::typing::type_compiled::matchers::StarlarkTypeIdMatcher;
4344
use crate::values::typing::type_compiled::type_matcher_factory::TypeMatcherFactory;
4445

@@ -147,6 +148,7 @@ pub trait TypeMatcherAlloc: Sized {
147148
TyBasic::Dict(k, v) => self.dict_of(k, v),
148149
TyBasic::Iter(_item) => self.alloc(IsIterable),
149150
TyBasic::Callable => self.alloc(IsCallable),
151+
TyBasic::Type => self.alloc(IsType),
150152
TyBasic::Custom(custom) => self.custom(custom),
151153
}
152154
}

0 commit comments

Comments
 (0)