this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "type-builtins.h"
3
4#include "cpython-types.h"
5#include "gtest/gtest.h"
6
7#include "attributedict.h"
8#include "builtins.h"
9#include "dict-builtins.h"
10#include "handles.h"
11#include "ic.h"
12#include "module-builtins.h"
13#include "objects.h"
14#include "runtime.h"
15#include "str-builtins.h"
16#include "test-utils.h"
17
18namespace py {
19namespace testing {
20
21using TypeBuiltinsDeathTest = RuntimeFixture;
22using TypeBuiltinsTest = RuntimeFixture;
23
24TEST_F(TypeBuiltinsTest, TypeAtReturnsNoPlaceholderValue) {
25 HandleScope scope(thread_);
26 Type type(&scope, runtime_->newType());
27 Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__"));
28 Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value"));
29 typeAtPut(thread_, type, name, value);
30 EXPECT_EQ(typeAt(type, name), *value);
31 EXPECT_EQ(typeAtById(thread_, type, ID(__ne__)), *value);
32}
33
34TEST_F(TypeBuiltinsTest, TypeAtReturnsErrorNotFoundForPlaceholder) {
35 HandleScope scope(thread_);
36 Type type(&scope, runtime_->newType());
37 Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__"));
38 Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value"));
39 ValueCell value_cell(&scope, typeAtPut(thread_, type, name, value));
40 value_cell.makePlaceholder();
41 EXPECT_TRUE(typeAt(type, name).isErrorNotFound());
42 EXPECT_TRUE(typeAtById(thread_, type, ID(__ne__)).isErrorNotFound());
43}
44
45TEST_F(TypeBuiltinsTest, TypeAtPutPutsValueInValueCell) {
46 HandleScope scope(thread_);
47 Type type(&scope, runtime_->newType());
48 Object name(&scope, Runtime::internStrFromCStr(thread_, "__ne__"));
49 Object value(&scope, Runtime::internStrFromCStr(thread_, "__ne__'s value"));
50
51 ValueCell result(&scope, typeAtPut(thread_, type, name, value));
52 ASSERT_EQ(result.value(), *value);
53 EXPECT_EQ(typeAt(type, name), *value);
54 result.setValue(NoneType::object());
55
56 result = typeAtPutById(thread_, type, ID(__ne__), value);
57 ASSERT_EQ(result.value(), *value);
58 EXPECT_EQ(typeAtById(thread_, type, ID(__ne__)), *value);
59}
60
61TEST_F(TypeBuiltinsTest, TypeAtPutByStrInvalidatesCache) {
62 ASSERT_FALSE(runFromCStr(runtime_, R"(
63class A:
64 def foo(self): return 4
65
66def cache_a_foo(a):
67 return a.foo
68
69a = A()
70cache_a_foo(a)
71)")
72 .isError());
73 HandleScope scope(thread_);
74 Function cache_a_foo(&scope, mainModuleAt(runtime_, "cache_a_foo"));
75 MutableTuple caches(&scope, cache_a_foo.caches());
76 Object a(&scope, mainModuleAt(runtime_, "a"));
77 ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
78
79 Type type_a(&scope, mainModuleAt(runtime_, "A"));
80 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
81 Object none(&scope, NoneType::object());
82 typeAtPut(thread_, type_a, foo, none);
83 EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
84}
85
86TEST_F(TypeBuiltinsTest, TypeAtPutByIdInvalidatesCache) {
87 ASSERT_FALSE(runFromCStr(runtime_, R"(
88class A:
89 def __eq__(self, other): return True
90
91def cache_a_eq(a):
92 return a.__eq__
93
94a = A()
95cache_a_eq(a)
96)")
97 .isError());
98 HandleScope scope(thread_);
99 Function cache_a_eq(&scope, mainModuleAt(runtime_, "cache_a_eq"));
100 MutableTuple caches(&scope, cache_a_eq.caches());
101 Object a(&scope, mainModuleAt(runtime_, "a"));
102 ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
103
104 Type type_a(&scope, mainModuleAt(runtime_, "A"));
105 Object none(&scope, NoneType::object());
106 typeAtPutById(thread_, type_a, ID(__eq__), none);
107 EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
108}
109
110TEST_F(TypeBuiltinsTest, TypeAtPutDoesNotGrowOnTombstones) {
111 ASSERT_FALSE(runFromCStr(runtime_, R"(
112class C: pass
113)")
114 .isError());
115 HandleScope scope(thread_);
116 Type type(&scope, mainModuleAt(runtime_, "C"));
117 ASSERT_TRUE(type.attributes().isTuple());
118 word initial_capacity = Tuple::cast(type.attributes()).length();
119 // Insert and remove symbols to fill the dictionary with tombstones.
120 Object name(&scope, NoneType::object());
121 for (word i = 0; i < static_cast<word>(SymbolId::kMaxId); i++) {
122 name = runtime_->symbols()->at(static_cast<SymbolId>(i));
123 attributeValueCellAtPut(thread_, type, name);
124 typeRemove(thread_, type, name);
125 }
126 EXPECT_EQ(Tuple::cast(type.attributes()).length(), initial_capacity);
127}
128
129TEST_F(TypeBuiltinsTest, TypeRemoveForNonExistingEntryReturnsErrorNotFound) {
130 ASSERT_FALSE(runFromCStr(runtime_, R"(
131class A:
132 def __eq__(self, other): return True
133)")
134 .isError());
135 HandleScope scope(thread_);
136 Type type(&scope, mainModuleAt(runtime_, "A"));
137 Str dunder_gt(&scope, runtime_->newStrFromCStr("__gt__"));
138 EXPECT_TRUE(typeRemove(thread_, type, dunder_gt).isErrorNotFound());
139}
140
141TEST_F(TypeBuiltinsTest, TypeRemoveRemovesAssociatedEntry) {
142 ASSERT_FALSE(runFromCStr(runtime_, R"(
143class A:
144 def __eq__(self, other): return True
145)")
146 .isError());
147 HandleScope scope(thread_);
148 Type type(&scope, mainModuleAt(runtime_, "A"));
149 Object dunder_eq(&scope, Runtime::internStrFromCStr(thread_, "__eq__"));
150 ASSERT_FALSE(typeAt(type, dunder_eq).isErrorNotFound());
151 ASSERT_FALSE(typeRemove(thread_, type, dunder_eq).isErrorNotFound());
152 EXPECT_TRUE(typeAt(type, dunder_eq).isErrorNotFound());
153}
154
155TEST_F(TypeBuiltinsTest, TypeRemoveInvalidatesCache) {
156 ASSERT_FALSE(runFromCStr(runtime_, R"(
157class A:
158 def __eq__(self, other): return True
159
160def cache_a_eq(a):
161 return a.__eq__
162
163a = A()
164cache_a_eq(a)
165)")
166 .isError());
167 HandleScope scope(thread_);
168 Function cache_a_eq(&scope, mainModuleAt(runtime_, "cache_a_eq"));
169 MutableTuple caches(&scope, cache_a_eq.caches());
170 Object a(&scope, mainModuleAt(runtime_, "a"));
171 ASSERT_FALSE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
172
173 Type type_a(&scope, mainModuleAt(runtime_, "A"));
174 Str dunder_eq(&scope, runtime_->newStrFromCStr("__eq__"));
175 ASSERT_FALSE(typeRemove(thread_, type_a, dunder_eq).isErrorNotFound());
176 EXPECT_TRUE(icLookupAttr(*caches, 1, a.layoutId()).isErrorNotFound());
177}
178
179TEST_F(TypeBuiltinsTest, TypeKeysFiltersOutPlaceholders) {
180 HandleScope scope(thread_);
181 Type type(&scope, runtime_->newType());
182
183 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
184 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
185 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
186 Str value(&scope, runtime_->newStrFromCStr("value"));
187
188 typeAtPut(thread_, type, foo, value);
189 typeAtPut(thread_, type, bar, value);
190 typeAtPut(thread_, type, baz, value);
191
192 ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder();
193
194 List keys(&scope, typeKeys(thread_, type));
195 EXPECT_EQ(keys.numItems(), 2);
196 EXPECT_EQ(keys.at(0), *foo);
197 EXPECT_EQ(keys.at(1), *baz);
198}
199
200TEST_F(TypeBuiltinsTest, TypeLenReturnsItemCountExcludingPlaceholders) {
201 HandleScope scope(thread_);
202 Type type(&scope, runtime_->newType());
203
204 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
205 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
206 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
207 Str value(&scope, runtime_->newStrFromCStr("value"));
208
209 typeAtPut(thread_, type, foo, value);
210 typeAtPut(thread_, type, bar, value);
211 typeAtPut(thread_, type, baz, value);
212
213 word previous_len = typeLen(thread_, type);
214
215 ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder();
216
217 word after_len = typeLen(thread_, type);
218 EXPECT_EQ(previous_len, after_len + 1);
219}
220
221TEST_F(TypeBuiltinsTest, TypeValuesFiltersOutPlaceholders) {
222 HandleScope scope(thread_);
223 Type type(&scope, runtime_->newType());
224
225 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
226 Object foo_value(&scope, runtime_->newStrFromCStr("foo_value"));
227 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
228 Object bar_value(&scope, runtime_->newStrFromCStr("bar_value"));
229 Object baz(&scope, Runtime::internStrFromCStr(thread_, "baz"));
230 Object baz_value(&scope, runtime_->newStrFromCStr("baz_value"));
231
232 typeAtPut(thread_, type, foo, foo_value);
233 typeAtPut(thread_, type, bar, bar_value);
234 typeAtPut(thread_, type, baz, baz_value);
235
236 ValueCell::cast(typeValueCellAt(*type, *bar)).makePlaceholder();
237
238 List values(&scope, typeValues(thread_, type));
239 EXPECT_TRUE(listContains(values, foo_value));
240 EXPECT_FALSE(listContains(values, bar_value));
241 EXPECT_TRUE(listContains(values, baz_value));
242}
243
244TEST_F(TypeBuiltinsTest, DunderBasesReturnsTuple) {
245 HandleScope scope(thread_);
246 ASSERT_FALSE(runFromCStr(runtime_, R"(
247class A: pass
248class B: pass
249class C(A, B): pass
250)")
251 .isError());
252 Object a(&scope, mainModuleAt(runtime_, "A"));
253 Object b(&scope, mainModuleAt(runtime_, "B"));
254 Object c(&scope, mainModuleAt(runtime_, "C"));
255 Object result_obj(&scope,
256 runtime_->attributeAtById(thread_, c, ID(__bases__)));
257 ASSERT_TRUE(result_obj.isTuple());
258 Tuple result(&scope, *result_obj);
259 ASSERT_EQ(result.length(), 2);
260 EXPECT_EQ(result.at(0), a);
261 EXPECT_EQ(result.at(1), b);
262}
263
264TEST_F(TypeBuiltinsTest, DunderBasesOnObjectReturnsEmptyTuple) {
265 HandleScope scope(thread_);
266 Object type(&scope, runtime_->typeAt(LayoutId::kObject));
267 Object result_obj(&scope,
268 runtime_->attributeAtById(thread_, type, ID(__bases__)));
269 ASSERT_TRUE(result_obj.isTuple());
270 EXPECT_EQ(Tuple::cast(*result_obj).length(), 0);
271}
272
273TEST_F(TypeBuiltinsTest, DunderBasesOnBuiltinTypeReturnsTuple) {
274 HandleScope scope(thread_);
275 Object type(&scope, runtime_->typeAt(LayoutId::kInt));
276 Object result_obj(&scope,
277 runtime_->attributeAtById(thread_, type, ID(__bases__)));
278 ASSERT_TRUE(result_obj.isTuple());
279 Tuple result(&scope, *result_obj);
280 ASSERT_EQ(result.length(), 1);
281 EXPECT_EQ(result.at(0), runtime_->typeAt(LayoutId::kObject));
282}
283
284TEST_F(TypeBuiltinsTest, DunderCallType) {
285 HandleScope scope(thread_);
286
287 ASSERT_FALSE(runFromCStr(runtime_, R"(
288class C: pass
289c = C()
290)")
291 .isError());
292
293 Type type(&scope, mainModuleAt(runtime_, "C"));
294 ASSERT_FALSE(type.isError());
295 Object instance(&scope, mainModuleAt(runtime_, "c"));
296 ASSERT_FALSE(instance.isError());
297 Object instance_type(&scope, runtime_->typeOf(*instance));
298 ASSERT_FALSE(instance_type.isError());
299
300 EXPECT_EQ(*type, *instance_type);
301}
302
303TEST_F(TypeBuiltinsTest, DunderCallTypeWithInit) {
304 HandleScope scope(thread_);
305
306 ASSERT_FALSE(runFromCStr(runtime_, R"(
307class C:
308 def __init__(self):
309 global g
310 g = 2
311
312g = 1
313C()
314)")
315 .isError());
316
317 Object global(&scope, mainModuleAt(runtime_, "g"));
318 ASSERT_FALSE(global.isError());
319 EXPECT_TRUE(isIntEqualsWord(*global, 2));
320}
321
322TEST_F(TypeBuiltinsTest, DunderCallTypeWithInitAndArgs) {
323 HandleScope scope(thread_);
324
325 ASSERT_FALSE(runFromCStr(runtime_, R"(
326class C:
327 def __init__(self, x):
328 global g
329 g = x
330
331g = 1
332C(9)
333)")
334 .isError());
335
336 Object global(&scope, mainModuleAt(runtime_, "g"));
337 ASSERT_FALSE(global.isError());
338 EXPECT_TRUE(isIntEqualsWord(*global, 9));
339}
340
341TEST_F(TypeBuiltinsTest,
342 DunderCallWithNonTypeDudnerNewResultReturnsWithoutCallingDunderInit) {
343 HandleScope scope(thread_);
344 ASSERT_FALSE(runFromCStr(runtime_, R"(
345class C:
346 def __new__(self, *args):
347 return 17
348 def __init__(self, *args):
349 raise Exception("should not happen")
350result = type.__call__(C, "C", (), {})
351)")
352 .isError());
353 Object result(&scope, mainModuleAt(runtime_, "result"));
354 EXPECT_TRUE(isIntEqualsWord(*result, 17));
355}
356
357TEST_F(TypeBuiltinsTest, DunderDirReturnsList) {
358 HandleScope scope(thread_);
359 ASSERT_FALSE(runFromCStr(runtime_, R"(
360class A:
361 x = 42
362 def foo(): pass
363class B(A):
364 def bar(): pass
365dir = type.__dir__(B)
366)")
367 .isError());
368 Object dir(&scope, mainModuleAt(runtime_, "dir"));
369 Object x(&scope, Runtime::internStrFromCStr(thread_, "x"));
370 EXPECT_TRUE(listContains(dir, x));
371 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
372 EXPECT_TRUE(listContains(dir, foo));
373 Object bar(&scope, Runtime::internStrFromCStr(thread_, "bar"));
374 EXPECT_TRUE(listContains(dir, bar));
375}
376
377TEST_F(TypeBuiltinsTest, DunderDocOnEmptyTypeReturnsNone) {
378 HandleScope scope(thread_);
379 ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError());
380 Object c(&scope, mainModuleAt(runtime_, "C"));
381 Object doc(&scope, runtime_->attributeAtById(thread_, c, ID(__doc__)));
382 EXPECT_EQ(doc, NoneType::object());
383}
384
385TEST_F(TypeBuiltinsTest, DunderDocReturnsDocumentationString) {
386 HandleScope scope(thread_);
387 ASSERT_FALSE(runFromCStr(runtime_, R"(
388class C:
389 """hello documentation"""
390 pass
391)")
392 .isError());
393 Object c(&scope, mainModuleAt(runtime_, "C"));
394 Object doc(&scope, runtime_->attributeAtById(thread_, c, ID(__doc__)));
395 EXPECT_TRUE(isStrEqualsCStr(*doc, "hello documentation"));
396}
397
398TEST_F(TypeBuiltinsTest, DunderGetattributeReturnsAttribute) {
399 HandleScope scope(thread_);
400 ASSERT_FALSE(runFromCStr(runtime_, R"(
401class C:
402 foo = -13
403)")
404 .isError());
405 Object c(&scope, mainModuleAt(runtime_, "C"));
406 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
407 EXPECT_TRUE(
408 isIntEqualsWord(runBuiltin(METH(type, __getattribute__), c, name), -13));
409}
410
411TEST_F(TypeBuiltinsTest, DunderGetattributeWithNonStringNameRaisesTypeError) {
412 HandleScope scope(thread_);
413 ASSERT_FALSE(runFromCStr(runtime_, R"(
414class C:
415 pass
416)")
417 .isError());
418 Object c(&scope, mainModuleAt(runtime_, "C"));
419 Object name(&scope, runtime_->newInt(0));
420 EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __getattribute__), c, name),
421 LayoutId::kTypeError,
422 "attribute name must be string, not 'int'"));
423}
424
425TEST_F(TypeBuiltinsTest,
426 DunderGetattributeWithMissingAttributeRaisesAttributeError) {
427 HandleScope scope(thread_);
428 ASSERT_FALSE(runFromCStr(runtime_, R"(
429class C:
430 pass
431)")
432 .isError());
433 Object c(&scope, mainModuleAt(runtime_, "C"));
434 Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx"));
435 EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __getattribute__), c, name),
436 LayoutId::kAttributeError,
437 "type object 'C' has no attribute 'xxx'"));
438}
439
440TEST_F(TypeBuiltinsTest, DunderReprForBuiltinReturnsStr) {
441 ASSERT_FALSE(
442 runFromCStr(runtime_, "result = type.__repr__(object)").isError());
443 EXPECT_TRUE(
444 isStrEqualsCStr(mainModuleAt(runtime_, "result"), "<class 'object'>"));
445}
446
447TEST_F(TypeBuiltinsTest, DunderReprForUserDefinedTypeReturnsStr) {
448 ASSERT_FALSE(runFromCStr(runtime_, R"(
449class Foo:
450 pass
451result = type.__repr__(Foo)
452)")
453 .isError());
454 EXPECT_TRUE(isStrEqualsCStr(mainModuleAt(runtime_, "result"),
455 "<class '__main__.Foo'>"));
456}
457
458TEST_F(TypeBuiltinsTest, DunderNewWithOneArgReturnsTypeOfArg) {
459 HandleScope scope(thread_);
460 ASSERT_FALSE(runFromCStr(runtime_, R"(
461a = type.__new__(type, 1);
462b = type.__new__(type, "hello");
463)")
464 .isError());
465 Type a(&scope, mainModuleAt(runtime_, "a"));
466 Type b(&scope, mainModuleAt(runtime_, "b"));
467
468 EXPECT_EQ(a.instanceLayoutId(), LayoutId::kInt);
469 EXPECT_EQ(b.instanceLayoutId(), LayoutId::kStr);
470}
471
472TEST_F(TypeBuiltinsTest, DunderNewWithOneMetaclassArgReturnsType) {
473 HandleScope scope(thread_);
474 ASSERT_FALSE(runFromCStr(runtime_, R"(
475class Foo(type):
476 pass
477a = type.__new__(type, Foo);
478)")
479 .isError());
480 Type a(&scope, mainModuleAt(runtime_, "a"));
481 EXPECT_EQ(a.instanceLayoutId(), LayoutId::kType);
482}
483
484TEST_F(TypeBuiltinsTest, DunderSetattrSetsAttribute) {
485 HandleScope scope(thread_);
486 ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError());
487 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
488 ASSERT_TRUE(c_obj.isType());
489 Type c(&scope, *c_obj);
490 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
491 Object value(&scope, runtime_->newInt(-7331));
492 EXPECT_TRUE(runBuiltin(METH(type, __setattr__), c, name, value).isNoneType());
493 EXPECT_TRUE(isIntEqualsWord(typeAt(c, name), -7331));
494}
495
496TEST_F(TypeBuiltinsTest, DunderSetattrWithNonStrNameRaisesTypeError) {
497 HandleScope scope(thread_);
498 ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError());
499 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
500 ASSERT_TRUE(c_obj.isType());
501 Type c(&scope, *c_obj);
502 Object name(&scope, NoneType::object());
503 Object value(&scope, runtime_->newInt(1));
504 EXPECT_TRUE(raisedWithStr(runBuiltin(METH(type, __setattr__), c, name, value),
505 LayoutId::kTypeError,
506 "attribute name must be string, not 'NoneType'"));
507}
508
509TEST_F(TypeBuiltinsTest, DunderSlotsCreatesLayoutWithInObjectAttributes) {
510 ASSERT_FALSE(runFromCStr(runtime_, R"(
511class C:
512 __slots__ = "x", "y", "z"
513)")
514 .isError());
515 HandleScope scope(thread_);
516 Type c(&scope, mainModuleAt(runtime_, "C"));
517 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasSlots));
518
519 Layout layout(&scope, c.instanceLayout());
520 EXPECT_TRUE(layout.isSealed());
521
522 Tuple attributes(&scope, layout.inObjectAttributes());
523 ASSERT_EQ(attributes.length(), 3);
524
525 Tuple elt0(&scope, attributes.at(0));
526 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
527 AttributeInfo info0(elt0.at(1));
528 EXPECT_TRUE(info0.isInObject());
529 EXPECT_TRUE(info0.isFixedOffset());
530
531 Tuple elt1(&scope, attributes.at(1));
532 EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y"));
533 AttributeInfo info1(elt1.at(1));
534 EXPECT_TRUE(info1.isInObject());
535 EXPECT_TRUE(info1.isFixedOffset());
536
537 Tuple elt2(&scope, attributes.at(2));
538 EXPECT_TRUE(isStrEqualsCStr(elt2.at(0), "z"));
539 AttributeInfo info2(elt2.at(1));
540 EXPECT_TRUE(info2.isInObject());
541 EXPECT_TRUE(info2.isFixedOffset());
542}
543
544TEST_F(TypeBuiltinsTest, DunderSlotsWithEmptyTupleCreatesEmptyLayout) {
545 ASSERT_FALSE(runFromCStr(runtime_, R"(
546class C:
547 __slots__ = ()
548)")
549 .isError());
550 HandleScope scope(thread_);
551 Type c(&scope, mainModuleAt(runtime_, "C"));
552 EXPECT_FALSE(c.hasFlag(Type::Flag::kHasSlots));
553
554 Layout layout(&scope, c.instanceLayout());
555 EXPECT_TRUE(layout.isSealed());
556
557 Tuple attributes(&scope, layout.inObjectAttributes());
558 ASSERT_EQ(attributes.length(), 0);
559}
560
561TEST_F(TypeBuiltinsTest, DunderSlotsAreInheritedFromLayoutBase) {
562 ASSERT_FALSE(runFromCStr(runtime_, R"(
563class C:
564 pass
565
566class D(C):
567 __slots__ = "x", "y"
568
569class E(D, C):
570 __slots__ = "z"
571)")
572 .isError());
573 HandleScope scope(thread_);
574 Type e(&scope, mainModuleAt(runtime_, "E"));
575 Layout layout(&scope, e.instanceLayout());
576 // The layout of E is not sealed due to C.
577 EXPECT_FALSE(layout.isSealed());
578 Tuple attributes(&scope, layout.inObjectAttributes());
579 // D is chosen as the layout base for E since D is a subtype of C.
580 ASSERT_EQ(attributes.length(), 3);
581
582 Tuple elt0(&scope, attributes.at(0));
583 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
584 AttributeInfo info0(elt0.at(1));
585 EXPECT_TRUE(info0.isInObject());
586 EXPECT_TRUE(info0.isFixedOffset());
587
588 Tuple elt1(&scope, attributes.at(1));
589 EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y"));
590 AttributeInfo info1(elt1.at(1));
591 EXPECT_TRUE(info1.isInObject());
592 EXPECT_TRUE(info1.isFixedOffset());
593
594 Tuple elt2(&scope, attributes.at(2));
595 EXPECT_TRUE(isStrEqualsCStr(elt2.at(0), "z"));
596 AttributeInfo info2(elt2.at(1));
597 EXPECT_TRUE(info2.isInObject());
598 EXPECT_TRUE(info2.isFixedOffset());
599}
600
601TEST_F(TypeBuiltinsTest, DunderSlotsSealsTypeWhenAllBasesAreSealed) {
602 ASSERT_FALSE(runFromCStr(runtime_, R"(
603class C:
604 __slots__ = ()
605
606class D:
607 __slots__ = ()
608
609class E(C, D):
610 __slots__ = "x"
611)")
612 .isError());
613 HandleScope scope(thread_);
614 Type e(&scope, mainModuleAt(runtime_, "E"));
615 Layout layout(&scope, e.instanceLayout());
616 EXPECT_TRUE(layout.isSealed());
617}
618
619TEST_F(TypeBuiltinsTest, DunderSlotsDoesNotSealTypeWhenBaseHasDunderDict) {
620 ASSERT_FALSE(runFromCStr(runtime_, R"(
621class C:
622 pass
623
624class D:
625 __slots__ = ()
626
627class E(C, D):
628 __slots__ = "x"
629)")
630 .isError());
631 HandleScope scope(thread_);
632 Type c(&scope, mainModuleAt(runtime_, "C"));
633 ASSERT_FALSE(Layout::cast(c.instanceLayout()).isSealed());
634
635 Type e(&scope, mainModuleAt(runtime_, "E"));
636 Layout layout(&scope, e.instanceLayout());
637 EXPECT_FALSE(layout.isSealed());
638}
639
640TEST_F(TypeBuiltinsTest,
641 DunderSlotsWithNonObjectBuiltinBaseAddsInObjectAttributes) {
642 ASSERT_FALSE(runFromCStr(runtime_, R"(
643class C(dict):
644 __slots__ = "x"
645)")
646 .isError());
647 HandleScope scope(thread_);
648 Layout dict_layout(&scope, runtime_->layoutAt(LayoutId::kDict));
649 ASSERT_TRUE(dict_layout.isSealed());
650 Tuple dict_attributes(&scope, dict_layout.inObjectAttributes());
651 ASSERT_EQ(dict_attributes.length(), 4);
652 Tuple dict_elt3(&scope, dict_attributes.at(3));
653 AttributeInfo dict_elt3_info(dict_elt3.at(1));
654 EXPECT_TRUE(dict_elt3_info.isInObject());
655 EXPECT_TRUE(dict_elt3_info.isFixedOffset());
656
657 Type c(&scope, mainModuleAt(runtime_, "C"));
658 Layout layout(&scope, c.instanceLayout());
659 // C's layout is sealed since it has only a sealed base.
660 EXPECT_TRUE(layout.isSealed());
661 Tuple attributes(&scope, layout.inObjectAttributes());
662 ASSERT_EQ(attributes.length(), 5);
663
664 Tuple elt0(&scope, attributes.at(4));
665 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
666 AttributeInfo info0(elt0.at(1));
667 EXPECT_TRUE(info0.isInObject());
668 EXPECT_TRUE(info0.isFixedOffset());
669 EXPECT_EQ(info0.offset(), dict_elt3_info.offset() + kPointerSize);
670}
671
672TEST_F(TypeBuiltinsTest,
673 DunderSlotsWithConflictingLayoutBasesOfUserTypeRaisesTypeError) {
674 ASSERT_FALSE(runFromCStr(runtime_, R"(
675class C:
676 __slots__ = "x"
677
678class D:
679 __slots__ = "y"
680)")
681 .isError());
682 ASSERT_TRUE(raisedWithStr(runFromCStr(runtime_, "class E(C, D): pass"),
683 LayoutId::kTypeError,
684 "multiple bases have instance lay-out conflict"));
685}
686
687TEST_F(TypeBuiltinsTest,
688 DunderSlotsWithConflictingLayoutBasesOfBuiltinTypeRaisesTypeError) {
689 // Confliction between a builtin type and a user-defined type.
690 ASSERT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
691class C:
692 __slots__ = "x" # This is conflicting with dict's in-object attributes.
693
694class D(C, dict):
695 pass
696)"),
697 LayoutId::kTypeError,
698 "multiple bases have instance lay-out conflict"));
699}
700
701TEST_F(TypeBuiltinsTest,
702 DunderSlotsWithEmptyTupleDoesNotConflictWithOtherDunderSlots) {
703 ASSERT_FALSE(runFromCStr(runtime_, R"(
704class C:
705 __slots__ = ()
706
707class D:
708 __slots__ = "x"
709
710class E(C, D):
711 pass
712)")
713 .isError());
714 HandleScope scope(thread_);
715 Type e(&scope, mainModuleAt(runtime_, "E"));
716 Layout layout(&scope, e.instanceLayout());
717 Tuple attributes(&scope, layout.inObjectAttributes());
718 ASSERT_EQ(attributes.length(), 1);
719
720 Tuple elt0(&scope, attributes.at(0));
721 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
722 AttributeInfo info0(elt0.at(1));
723 EXPECT_TRUE(info0.isInObject());
724 EXPECT_TRUE(info0.isFixedOffset());
725}
726
727TEST_F(TypeBuiltinsTest, DunderSlotsSharingSameLayoutBaseCanServceAsBases) {
728 // Although F's bases, D, E do not appear in the same type hierarchy
729 // (neither D is a subtype of E nor E is a subtype of D), but
730 // D's layout base (C) is the supertype of E, which makes the type checking
731 // succeed.
732 ASSERT_FALSE(runFromCStr(runtime_, R"(
733class C:
734 __slots__ = "x"
735
736class D(C):
737 pass
738
739class E(C):
740 __slots__ = "y"
741
742class F(D, E):
743 pass
744)")
745 .isError());
746 HandleScope scope(thread_);
747 Type f(&scope, mainModuleAt(runtime_, "F"));
748 Layout layout(&scope, f.instanceLayout());
749 Tuple attributes(&scope, layout.inObjectAttributes());
750 ASSERT_EQ(attributes.length(), 2);
751
752 Tuple elt0(&scope, attributes.at(0));
753 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
754 AttributeInfo info0(elt0.at(1));
755 EXPECT_TRUE(info0.isInObject());
756 EXPECT_TRUE(info0.isFixedOffset());
757
758 Tuple elt1(&scope, attributes.at(1));
759 EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y"));
760 AttributeInfo info1(elt1.at(1));
761 EXPECT_TRUE(info1.isInObject());
762 EXPECT_TRUE(info1.isFixedOffset());
763}
764
765TEST_F(TypeBuiltinsTest, DunderSlotsPopulatesSlotDescriptorsWithCorrectValues) {
766 ASSERT_FALSE(runFromCStr(runtime_, R"(
767class C:
768 __slots__ = "x"
769
770class D(C):
771 __slots__ = "y"
772)")
773 .isError());
774 HandleScope scope(thread_);
775 Type c(&scope, mainModuleAt(runtime_, "C"));
776 Layout c_layout(&scope, c.instanceLayout());
777 // Checking descriptor for "x" in C.
778 {
779 Str x(&scope, runtime_->newStrFromCStr("x"));
780 SlotDescriptor descriptor_x(&scope, typeAt(c, x));
781 EXPECT_EQ(descriptor_x.type(), *c);
782 EXPECT_TRUE(isStrEqualsCStr(descriptor_x.name(), "x"));
783 AttributeInfo info;
784 ASSERT_TRUE(Runtime::layoutFindAttribute(*c_layout, x, &info));
785 EXPECT_EQ(descriptor_x.offset(), info.offset());
786 }
787
788 Type d(&scope, mainModuleAt(runtime_, "D"));
789 Layout d_layout(&scope, d.instanceLayout());
790 // Checking descriptors for "x" and "y" in D.
791 {
792 // "x" is inherited from C to D.
793 Str x(&scope, runtime_->newStrFromCStr("x"));
794 ASSERT_TRUE(typeAt(d, x).isErrorNotFound());
795 SlotDescriptor descriptor_x(&scope, typeLookupInMro(thread_, *d, *x));
796 EXPECT_EQ(descriptor_x.type(), *c);
797 EXPECT_TRUE(isStrEqualsCStr(descriptor_x.name(), "x"));
798 AttributeInfo info;
799 ASSERT_TRUE(Runtime::layoutFindAttribute(*d_layout, x, &info));
800 EXPECT_EQ(descriptor_x.offset(), info.offset());
801
802 // "y" is populated in D itself.
803 Str y(&scope, runtime_->newStrFromCStr("y"));
804 SlotDescriptor descriptor_y(&scope, typeAt(d, y));
805 EXPECT_EQ(descriptor_y.type(), *d);
806 EXPECT_TRUE(isStrEqualsCStr(descriptor_y.name(), "y"));
807 ASSERT_TRUE(Runtime::layoutFindAttribute(*d_layout, y, &info));
808 EXPECT_EQ(descriptor_y.offset(), info.offset());
809 }
810}
811
812static RawObject newExtensionType(PyObject* extension_type) {
813 Thread* thread = Thread::current();
814 Runtime* runtime = thread->runtime();
815 HandleScope scope(thread);
816
817 Str name(&scope, runtime->newStrFromCStr("ExtType"));
818 Object object_type(&scope, runtime->typeAt(LayoutId::kObject));
819 Tuple bases(&scope, runtime->newTupleWith1(object_type));
820 Dict dict(&scope, runtime->newDict());
821 Type metaclass(&scope, runtime->typeAt(LayoutId::kType));
822 Type type(&scope,
823 typeNew(thread, metaclass, name, bases, dict,
824 Type::Flag::kHasNativeData | Type::Flag::kIsBasetype,
825 /*inherit_slots=*/false,
826 /*add_instance_dict=*/false));
827
828 extension_type->reference_ = type.raw();
829 return *type;
830}
831
832TEST_F(TypeBuiltinsTest,
833 DunderSlotsWithExtensionTypeAsBaseAllocatesExtraSpace) {
834 // Create a main module.
835 ASSERT_FALSE(runFromCStr(runtime_, "").isError());
836 HandleScope scope(thread_);
837 PyObject extension_type;
838 Type type(&scope, newExtensionType(&extension_type));
839 ASSERT_TRUE(type.hasFlag(Type::Flag::kHasNativeData));
840
841 Module main_module(&scope, findMainModule(runtime_));
842 Str type_name(&scope, runtime_->newStrFromCStr("ExtType"));
843 moduleAtPut(thread_, main_module, type_name, type);
844
845 ASSERT_FALSE(runFromCStr(runtime_, R"(
846class C(ExtType):
847 __slots__ = "x", "y"
848)")
849 .isError());
850 Type c(&scope, mainModuleAt(runtime_, "C"));
851 ASSERT_TRUE(c.hasFlag(Type::Flag::kHasNativeData));
852
853 Layout layout(&scope, c.instanceLayout());
854 Tuple attributes(&scope, layout.inObjectAttributes());
855 // "x" and "y" are added as a regular attribute.
856 ASSERT_EQ(attributes.length(), 2);
857 // However, more speace was allocated to be a native proxy.
858 EXPECT_EQ(layout.instanceSize(),
859 2 * kPointerSize + NativeProxy::kSizeFromEnd);
860
861 Tuple elt0(&scope, attributes.at(0));
862 EXPECT_TRUE(isStrEqualsCStr(elt0.at(0), "x"));
863 AttributeInfo info0(elt0.at(1));
864 EXPECT_TRUE(info0.isInObject());
865 EXPECT_TRUE(info0.isFixedOffset());
866
867 Tuple elt1(&scope, attributes.at(1));
868 EXPECT_TRUE(isStrEqualsCStr(elt1.at(0), "y"));
869 AttributeInfo info1(elt1.at(1));
870 EXPECT_TRUE(info1.isInObject());
871 EXPECT_TRUE(info1.isFixedOffset());
872}
873
874TEST_F(TypeBuiltinsTest, TypeHasDunderMroAttribute) {
875 HandleScope scope(thread_);
876 ASSERT_FALSE(
877 runFromCStr(runtime_, "result = str.__class__.__mro__").isError());
878 Object result(&scope, mainModuleAt(runtime_, "result"));
879 ASSERT_TRUE(result.isTuple());
880}
881
882TEST_F(TypeBuiltinsTest, TypeHasDunderNameAttribute) {
883 HandleScope scope(thread_);
884 ASSERT_FALSE(
885 runFromCStr(runtime_, "result = str.__class__.__name__").isError());
886 Object result(&scope, mainModuleAt(runtime_, "result"));
887 EXPECT_TRUE(isStrEqualsCStr(*result, "type"));
888}
889
890TEST_F(TypeBuiltinsTest, TypeHasDunderDictAttribute) {
891 HandleScope scope(thread_);
892 ASSERT_FALSE(
893 runFromCStr(runtime_, "result = str.__class__.__dict__").isError());
894 Object result(&scope, mainModuleAt(runtime_, "result"));
895 EXPECT_TRUE(result.isMappingProxy());
896}
897
898TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsValue) {
899 HandleScope scope(thread_);
900 ASSERT_FALSE(runFromCStr(runtime_, R"(
901class A:
902 foo = 2
903)")
904 .isError());
905 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
906 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
907 Type a(&scope, *a_obj);
908 Object foo(&scope, Runtime::internStrFromCStr(thread_, "foo"));
909 EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *a, *foo), 2));
910}
911
912TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsParentValue) {
913 HandleScope scope(thread_);
914 ASSERT_FALSE(runFromCStr(runtime_, R"(
915class A:
916 foo = 2
917class B(A):
918 bar = 4
919)")
920 .isError());
921 Object b_obj(&scope, mainModuleAt(runtime_, "B"));
922 ASSERT_TRUE(b_obj.isType());
923 Type b(&scope, *b_obj);
924 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
925 EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *b, *name), 2));
926}
927
928TEST_F(TypeBuiltinsTest, TypeLookupNameInMroReturnsOverriddenValue) {
929 HandleScope scope(thread_);
930 ASSERT_FALSE(runFromCStr(runtime_, R"(
931class A:
932 foo = 2
933class B(A):
934 foo = 4
935)")
936 .isError());
937 Object b_obj(&scope, mainModuleAt(runtime_, "B"));
938 ASSERT_TRUE(b_obj.isType());
939 Type b(&scope, *b_obj);
940 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
941 EXPECT_TRUE(isIntEqualsWord(typeLookupInMro(thread_, *b, *name), 4));
942}
943
944TEST_F(TypeBuiltinsTest, TypeLookupNameInMroWithNonExistentNameReturnsError) {
945 HandleScope scope(thread_);
946 ASSERT_FALSE(runFromCStr(runtime_, R"(
947class A:
948 bar = 2
949)")
950 .isError());
951 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
952 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
953 Type a(&scope, *a_obj);
954 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
955 EXPECT_TRUE(typeLookupInMro(thread_, *a, *name).isError());
956 EXPECT_FALSE(thread_->hasPendingException());
957}
958
959TEST_F(TypeBuiltinsTest, TypeLookupSymbolInMroReturnsValue) {
960 HandleScope scope(thread_);
961 ASSERT_FALSE(runFromCStr(runtime_, R"(
962class A:
963 __add__ = 3
964)")
965 .isError());
966 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
967 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
968 Type a(&scope, *a_obj);
969 EXPECT_TRUE(
970 isIntEqualsWord(typeLookupInMroById(thread_, *a, ID(__add__)), 3));
971}
972
973TEST_F(TypeBuiltinsTest, DunderCallReceivesExArgs) {
974 ASSERT_FALSE(runFromCStr(runtime_, R"(
975class C:
976 def __init__(self, *args):
977 self.args = args
978
979 def num_args(self):
980 return len(self.args)
981
982result = C(*(1,2,3)).num_args()
983)")
984 .isError());
985 HandleScope scope(thread_);
986 Object result(&scope, mainModuleAt(runtime_, "result"));
987 EXPECT_EQ(*result, RawSmallInt::fromWord(3));
988}
989
990TEST_F(TypeBuiltinsTest, TypeNewWithNonStrKeyInDictRaisesTypeError) {
991 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
992ty = type.__new__(type, *("foo", (object,), {4: 1}))
993name = ty.__name__N
994)"),
995 LayoutId::kTypeError,
996 "attribute name must be string, not 'int'"));
997}
998
999TEST_F(TypeBuiltinsTest, ClassMethodDunderCallReceivesExArgs) {
1000 ASSERT_FALSE(runFromCStr(runtime_, R"(
1001class Foo:
1002 @classmethod
1003 def foo(cls, *args):
1004 return len(args)
1005
1006result = Foo.foo(*(1,2,3))
1007)")
1008 .isError());
1009 HandleScope scope(thread_);
1010 Object result(&scope, mainModuleAt(runtime_, "result"));
1011 EXPECT_EQ(*result, RawSmallInt::fromWord(3));
1012}
1013
1014TEST_F(TypeBuiltinsTest, TypeNewReceivesExArgs) {
1015 ASSERT_FALSE(runFromCStr(runtime_, R"(
1016ty = type.__new__(type, *("foo", (object,), {'a': 1}))
1017name = ty.__name__
1018)")
1019 .isError());
1020 HandleScope scope(thread_);
1021 Object name(&scope, mainModuleAt(runtime_, "name"));
1022 EXPECT_TRUE(isStrEqualsCStr(*name, "foo"));
1023}
1024
1025TEST_F(TypeBuiltinsTest, TypeCallWithInitReturningNonNoneRaisesTypeError) {
1026 EXPECT_TRUE(raisedWithStr(runFromCStr(runtime_, R"(
1027class C:
1028 def __init__(self, *args, **kwargs):
1029 return 5
1030C()
1031)"),
1032 LayoutId::kTypeError,
1033 "C.__init__ returned non None"));
1034}
1035
1036TEST_F(TypeBuiltinsTest, TypeGetAttributeReturnsAttributeValue) {
1037 HandleScope scope(thread_);
1038 ASSERT_FALSE(runFromCStr(runtime_, R"(
1039class C:
1040 x = 42
1041)")
1042 .isError());
1043 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
1044 ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj));
1045 Type c(&scope, *c_obj);
1046 Object name(&scope, Runtime::internStrFromCStr(thread_, "x"));
1047 EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, c, name), 42));
1048}
1049
1050TEST_F(TypeBuiltinsTest, TypeGetAttributeReturnsMetaclassAttributeValue) {
1051 HandleScope scope(thread_);
1052 ASSERT_FALSE(runFromCStr(runtime_, R"(
1053class M(type):
1054 x = 77
1055class C(metaclass=M): pass
1056)")
1057 .isError());
1058 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
1059 ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj));
1060 Type c(&scope, *c_obj);
1061 Object name(&scope, Runtime::internStrFromCStr(thread_, "x"));
1062 EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, c, name), 77));
1063}
1064
1065TEST_F(TypeBuiltinsTest, TypeGetAttributeWithMissingAttributeReturnsError) {
1066 HandleScope scope(thread_);
1067 ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError());
1068 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
1069 ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj));
1070 Type c(&scope, *c_obj);
1071 Object name(&scope, Runtime::internStrFromCStr(thread_, "xxx"));
1072 EXPECT_TRUE(typeGetAttribute(thread_, c, name).isError());
1073 EXPECT_FALSE(thread_->hasPendingException());
1074}
1075
1076TEST_F(TypeBuiltinsTest, TypeGetAttributeCallsDunderGetOnDataDescriptor) {
1077 HandleScope scope(thread_);
1078 ASSERT_FALSE(runFromCStr(runtime_, R"(
1079class D:
1080 def __set__(self, instance, value): pass
1081 def __get__(self, instance, owner): return (self, instance, owner)
1082class M(type):
1083 foo = D()
1084class A(metaclass=M): pass
1085)")
1086 .isError());
1087 Object d_obj(&scope, mainModuleAt(runtime_, "D"));
1088 ASSERT_TRUE(d_obj.isType());
1089 Type d(&scope, *d_obj);
1090 Object m(&scope, mainModuleAt(runtime_, "M"));
1091 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1092 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1093 Type a(&scope, *a_obj);
1094 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1095 Object result_obj(&scope, typeGetAttribute(thread_, a, name));
1096 ASSERT_TRUE(result_obj.isTuple());
1097 Tuple result(&scope, *result_obj);
1098 ASSERT_EQ(result.length(), 3);
1099 Type result_0_type(&scope, runtime_->typeOf(result.at(0)));
1100 EXPECT_TRUE(typeIsSubclass(*result_0_type, *d));
1101 EXPECT_EQ(result.at(1), a);
1102 EXPECT_EQ(result.at(2), m);
1103}
1104
1105TEST_F(TypeBuiltinsTest, TypeGetAttributeCallsDunderGetOnNonDataDescriptor) {
1106 HandleScope scope(thread_);
1107 ASSERT_FALSE(runFromCStr(runtime_, R"(
1108class D:
1109 def __get__(self, instance, owner): return 42
1110class M(type):
1111 foo = D()
1112class A(metaclass=M): pass
1113)")
1114 .isError());
1115 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1116 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1117 Type a(&scope, *a_obj);
1118 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1119 EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 42));
1120}
1121
1122TEST_F(TypeBuiltinsTest, TypeGetAttributePrefersDataDescriptorOverTypeAttr) {
1123 HandleScope scope(thread_);
1124 ASSERT_FALSE(runFromCStr(runtime_, R"(
1125class D:
1126 def __set__(self, instance, value): pass
1127 def __get__(self, instance, owner): return 42
1128class M(type):
1129 foo = D()
1130class A(metaclass=M):
1131 foo = 12
1132)")
1133 .isError());
1134 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1135 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1136 Type a(&scope, *a_obj);
1137 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1138 EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 42));
1139}
1140
1141TEST_F(TypeBuiltinsTest, TypeGetAttributePrefersFieldOverNonDataDescriptor) {
1142 HandleScope scope(thread_);
1143 ASSERT_FALSE(runFromCStr(runtime_, R"(
1144class D:
1145 def __get__(self, instance, owner): return 42
1146class M(type):
1147 foo = D()
1148class A(metaclass=M):
1149 foo = 12
1150)")
1151 .isError());
1152 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1153 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1154 Type a(&scope, *a_obj);
1155 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1156 EXPECT_TRUE(isIntEqualsWord(typeGetAttribute(thread_, a, name), 12));
1157}
1158
1159TEST_F(TypeBuiltinsTest, TypeGetAttributePropagatesDunderGetException) {
1160 HandleScope scope(thread_);
1161 ASSERT_FALSE(runFromCStr(runtime_, R"(
1162class D:
1163 def __set__(self, instance, value): pass
1164 def __get__(self, instance, owner): raise UserWarning()
1165class M(type):
1166 foo = D()
1167class A(metaclass=M): pass
1168)")
1169 .isError());
1170 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1171 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1172 Type a(&scope, *a_obj);
1173 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1174 EXPECT_TRUE(
1175 raised(typeGetAttribute(thread_, a, name), LayoutId::kUserWarning));
1176}
1177
1178TEST_F(TypeBuiltinsTest, TypeGetAttributeOnNoneTypeReturnsFunction) {
1179 HandleScope scope(thread_);
1180 Type none_type(&scope, runtime_->typeAt(LayoutId::kNoneType));
1181 Object name(&scope, Runtime::internStrFromCStr(thread_, "__repr__"));
1182 EXPECT_TRUE(typeGetAttribute(thread_, none_type, name).isFunction());
1183}
1184
1185TEST_F(TypeBuiltinsTest, TypeSetAttrSetsAttribute) {
1186 HandleScope scope(thread_);
1187 ASSERT_FALSE(runFromCStr(runtime_, "class C: pass").isError());
1188 Object c_obj(&scope, mainModuleAt(runtime_, "C"));
1189 ASSERT_TRUE(runtime_->isInstanceOfType(*c_obj));
1190 Type c(&scope, *c_obj);
1191 Object name(&scope, Runtime::internStrFromCStr(thread_, "foobarbaz"));
1192 Object value(&scope, runtime_->newInt(-444));
1193 EXPECT_TRUE(typeSetAttr(thread_, c, name, value).isNoneType());
1194 EXPECT_TRUE(isIntEqualsWord(typeAt(c, name), -444));
1195}
1196
1197TEST_F(TypeBuiltinsTest, TypeSetAttrCallsDunderSetOnDataDescriptor) {
1198 HandleScope scope(thread_);
1199 ASSERT_FALSE(runFromCStr(runtime_, R"(
1200class D:
1201 def __get__(self, instance, owner): pass
1202 def __set__(self, instance, value):
1203 global set_args
1204 set_args = (self, instance, value)
1205 return "ignored result"
1206foo = D()
1207class M(type):
1208 foo = foo
1209class A(metaclass=M):
1210 foo = "hidden by data descriptor"
1211)")
1212 .isError());
1213 Object foo(&scope, mainModuleAt(runtime_, "foo"));
1214 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1215 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1216 Type a(&scope, *a_obj);
1217 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1218 Object value(&scope, runtime_->newInt(77));
1219 EXPECT_TRUE(typeSetAttr(thread_, a, name, value).isNoneType());
1220 Object set_args_obj(&scope, mainModuleAt(runtime_, "set_args"));
1221 ASSERT_TRUE(set_args_obj.isTuple());
1222 Tuple set_args(&scope, *set_args_obj);
1223 ASSERT_EQ(set_args.length(), 3);
1224 EXPECT_EQ(set_args.at(0), foo);
1225 EXPECT_EQ(set_args.at(1), a);
1226 EXPECT_TRUE(isIntEqualsWord(set_args.at(2), 77));
1227}
1228
1229TEST_F(TypeBuiltinsTest, TypeSetAttrPropagatesDunderSetException) {
1230 HandleScope scope(thread_);
1231 ASSERT_FALSE(runFromCStr(runtime_, R"(
1232class D:
1233 def __get__(self, instance, owner): pass
1234 def __set__(self, instance, value): raise UserWarning()
1235class M(type):
1236 foo = D()
1237class A(metaclass=M):
1238 pass
1239)")
1240 .isError());
1241 Object a_obj(&scope, mainModuleAt(runtime_, "A"));
1242 ASSERT_TRUE(runtime_->isInstanceOfType(*a_obj));
1243 Type a(&scope, *a_obj);
1244 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1245 Object value(&scope, runtime_->newInt(1));
1246 EXPECT_TRUE(
1247 raised(typeSetAttr(thread_, a, name, value), LayoutId::kUserWarning));
1248}
1249
1250TEST_F(TypeBuiltinsTest, TypeSetAttrOnBuiltinTypeRaisesTypeError) {
1251 HandleScope scope(thread_);
1252 Type type(&scope, runtime_->typeAt(LayoutId::kInt));
1253 Object name(&scope, Runtime::internStrFromCStr(thread_, "foo"));
1254 Object value(&scope, NoneType::object());
1255 EXPECT_TRUE(raisedWithStr(
1256 typeSetAttr(thread_, type, name, value), LayoutId::kTypeError,
1257 "can't set attributes of built-in/extension type 'int'"));
1258}
1259
1260TEST_F(TypeBuiltinsTest, TypeofSmallStrReturnsStr) {
1261 ASSERT_FALSE(runFromCStr(runtime_, R"(
1262result = type('a')
1263)")
1264 .isError());
1265 EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kStr));
1266}
1267
1268TEST_F(TypeBuiltinsTest, TypeofLargeStrReturnsStr) {
1269 ASSERT_FALSE(runFromCStr(runtime_, R"(
1270result = type('aaaaaaaaaaaaaaaaaaaaaaa')
1271)")
1272 .isError());
1273 EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kStr));
1274}
1275
1276TEST_F(TypeBuiltinsTest, TypeofSmallIntReturnsInt) {
1277 ASSERT_FALSE(runFromCStr(runtime_, R"(
1278result = type(5)
1279)")
1280 .isError());
1281 EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kInt));
1282}
1283
1284TEST_F(TypeBuiltinsTest, TypeofLargeIntReturnsInt) {
1285 ASSERT_FALSE(runFromCStr(runtime_, R"(
1286result = type(99999999999999999999999999999999999999999)
1287)")
1288 .isError());
1289 EXPECT_EQ(mainModuleAt(runtime_, "result"), runtime_->typeAt(LayoutId::kInt));
1290}
1291
1292TEST_F(TypeBuiltinsTest, ResolveDescriptorGetReturnsNonDescriptor) {
1293 HandleScope scope(thread_);
1294
1295 Object instance(&scope, runtime_->newInt(123));
1296 Object owner(&scope, NoneType::object());
1297 Object descr(&scope, runtime_->newInt(456));
1298 EXPECT_EQ(resolveDescriptorGet(thread_, descr, instance, owner), *descr);
1299}
1300
1301TEST_F(TypeBuiltinsTest, ResolveDescriptorGetCallsDescriptorDunderGet) {
1302 HandleScope scope(thread_);
1303
1304 Object instance(&scope, runtime_->newInt(123));
1305 Type owner(&scope, runtime_->typeOf(*instance));
1306 Object descr(&scope, typeLookupInMroById(thread_, *owner, ID(__add__)));
1307 ASSERT_TRUE(descr.isFunction());
1308 EXPECT_TRUE(
1309 resolveDescriptorGet(thread_, descr, instance, owner).isBoundMethod());
1310}
1311
1312TEST_F(
1313 TypeBuiltinsTest,
1314 TerminateIfUnimplementedTypeAttrCacheInvalidationDoesNotTerminateRuntimeForSupportedCacheInvalidation) {
1315 // __len__ supports cache invalidation.
1316 EXPECT_FALSE(runFromCStr(runtime_, R"(
1317class C:
1318 def __len__(self): return 0
1319
1320C.__len__ = lambda self: 4
1321)")
1322 .isError());
1323
1324 // __setattr__ does not support cache invalidation, but it is not populated in
1325 // C so we do not terminate the runtime.
1326 EXPECT_FALSE(runFromCStr(runtime_, R"(
1327class C: pass
1328
1329C.__setattr__ = lambda self, key: 5
1330)")
1331 .isError());
1332}
1333
1334TEST_F(
1335 TypeBuiltinsDeathTest,
1336 TerminateIfUnimplementedTypeAttrCacheInvalidationTerminatesRuntimeForUnsupportedCacheInvalidation) {
1337 // Redefining the existing __setattr__ terminates the runtime due to the
1338 // unsupported cache invalidation for it.
1339 ASSERT_DEATH(static_cast<void>(runFromCStr(runtime_, R"(
1340class C:
1341 def __setattr__(self, key): pass
1342
1343C.__setattr__ = lambda self, key: 5
1344)")),
1345 "unimplemented cache invalidation for type.__setattr__ update");
1346}
1347
1348TEST_F(TypeBuiltinsTest, TypeIsMarkedAsCustomDict) {
1349 HandleScope scope(thread_);
1350 Type type(&scope, runtime_->typeAt(LayoutId::kType));
1351 EXPECT_TRUE(type.hasFlag(Type::Flag::kHasCustomDict));
1352 EXPECT_TRUE(Layout::cast(type.instanceLayout()).isSealed());
1353}
1354
1355TEST_F(TypeBuiltinsTest, TypeSubclassLayoutIsSealed) {
1356 HandleScope scope(thread_);
1357 EXPECT_FALSE(runFromCStr(runtime_, R"(
1358class Meta(type): pass
1359)")
1360 .isError());
1361 Type meta(&scope, mainModuleAt(runtime_, "Meta"));
1362 EXPECT_TRUE(meta.hasFlag(Type::Flag::kHasCustomDict));
1363 EXPECT_TRUE(Layout::cast(meta.instanceLayout()).isSealed());
1364}
1365
1366TEST_F(TypeBuiltinsTest, BuiltinTypesHaveAppropriateAttributeTypeFlags) {
1367 HandleScope scope(thread_);
1368 Type object_type(&scope, runtime_->typeAt(LayoutId::kObject));
1369 EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1370 EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1371 EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1372 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasStrDunderHash));
1373 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderBool));
1374 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderLen));
1375 EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderClass));
1376 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderGet));
1377 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderSet));
1378 EXPECT_FALSE(object_type.hasFlag(Type::Flag::kHasDunderDelete));
1379 EXPECT_TRUE(object_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1380
1381 Type type_type(&scope, runtime_->typeAt(LayoutId::kType));
1382 EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasTypeDunderGetattribute));
1383 EXPECT_FALSE(type_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1384 EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1385 EXPECT_TRUE(type_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1386
1387 Type module_type(&scope, runtime_->typeAt(LayoutId::kModule));
1388 EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasModuleDunderGetattribute));
1389 EXPECT_FALSE(module_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1390 EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1391 EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1392
1393 Type property_type(&scope, runtime_->typeAt(LayoutId::kProperty));
1394 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1395 EXPECT_FALSE(property_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1396 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1397 EXPECT_TRUE(module_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1398 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderGet));
1399 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderSet));
1400 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasDunderDelete));
1401 EXPECT_TRUE(property_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1402
1403 Type function_type(&scope, runtime_->typeAt(LayoutId::kFunction));
1404 EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1405 EXPECT_FALSE(function_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1406 EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1407 EXPECT_TRUE(function_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1408
1409 Type int_type(&scope, runtime_->typeAt(LayoutId::kInt));
1410 EXPECT_TRUE(int_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1411 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1412 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1413 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasStrDunderHash));
1414 EXPECT_TRUE(int_type.hasFlag(Type::Flag::kHasDunderBool));
1415 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderLen));
1416 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderGet));
1417 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderSet));
1418 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasDunderDelete));
1419 EXPECT_FALSE(int_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1420
1421 Type str_type(&scope, runtime_->typeAt(LayoutId::kStr));
1422 EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1423 EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1424 EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1425 // TODO(T83275120): Flip the condition.
1426 EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasDunderBool));
1427 EXPECT_TRUE(str_type.hasFlag(Type::Flag::kHasDunderLen));
1428 EXPECT_FALSE(str_type.hasFlag(Type::Flag::kHasObjectDunderEq));
1429
1430 // super.__getattribute__ is not same as object.__getattribute.
1431 Type super_type(&scope, runtime_->typeAt(LayoutId::kSuper));
1432 EXPECT_FALSE(super_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1433 EXPECT_FALSE(super_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1434 EXPECT_TRUE(super_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1435
1436 // BaseException inherits object.__new__.
1437 Type base_exception_type(&scope, runtime_->typeAt(LayoutId::kBaseException));
1438 EXPECT_TRUE(
1439 base_exception_type.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1440 EXPECT_TRUE(base_exception_type.hasFlag(Type::Flag::kHasObjectDunderNew));
1441 EXPECT_TRUE(base_exception_type.hasFlag(Type::Flag::kHasObjectDunderHash));
1442
1443 // NoneType.__class__'s behavior is same as object.__class__ although
1444 // they point to different objects.
1445 Type none_type(&scope, runtime_->typeAt(LayoutId::kNoneType));
1446 EXPECT_TRUE(none_type.hasFlag(Type::Flag::kHasObjectDunderClass));
1447}
1448
1449TEST_F(TypeBuiltinsTest, UserTypesHaveAttributeTypeFlags) {
1450 HandleScope scope(thread_);
1451 EXPECT_FALSE(runFromCStr(runtime_, R"(
1452class C: pass
1453
1454class D(type): pass
1455
1456class E(module): pass
1457
1458class F:
1459 def __new__(cls): return None
1460
1461class G:
1462 def __hash__(self): return 10
1463
1464class H:
1465 def __bool__(self): return False
1466
1467class I:
1468 def __len__(self): return 10
1469
1470class J:
1471 __class__ = None
1472
1473class K:
1474 def __get__(self, owner, type):
1475 return None
1476
1477class L:
1478 def __set__(self, owner, value):
1479 return None
1480
1481class M:
1482 def __delete__(self, obj):
1483 return None
1484
1485class N:
1486 def __eq__(self, other):
1487 return None
1488
1489class SubN(N):
1490 pass
1491
1492class Str(str): pass
1493
1494class Str2(Str):
1495 def __hash__(self): return 10
1496
1497)")
1498 .isError());
1499 Type c(&scope, mainModuleAt(runtime_, "C"));
1500 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1501 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderNew));
1502 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderHash));
1503 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderClass));
1504 EXPECT_TRUE(c.hasFlag(Type::Flag::kHasObjectDunderEq));
1505
1506 Type d(&scope, mainModuleAt(runtime_, "D"));
1507 EXPECT_TRUE(d.hasFlag(Type::Flag::kHasTypeDunderGetattribute));
1508 EXPECT_FALSE(d.hasFlag(Type::Flag::kHasObjectDunderNew));
1509 EXPECT_TRUE(d.hasFlag(Type::Flag::kHasObjectDunderHash));
1510
1511 Type e(&scope, mainModuleAt(runtime_, "E"));
1512 EXPECT_TRUE(e.hasFlag(Type::Flag::kHasModuleDunderGetattribute));
1513 EXPECT_FALSE(e.hasFlag(Type::Flag::kHasObjectDunderNew));
1514 EXPECT_TRUE(e.hasFlag(Type::Flag::kHasObjectDunderHash));
1515
1516 Type f(&scope, mainModuleAt(runtime_, "F"));
1517 EXPECT_TRUE(f.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1518 EXPECT_FALSE(f.hasFlag(Type::Flag::kHasObjectDunderNew));
1519 EXPECT_TRUE(f.hasFlag(Type::Flag::kHasObjectDunderHash));
1520
1521 Type g(&scope, mainModuleAt(runtime_, "G"));
1522 EXPECT_TRUE(g.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1523 EXPECT_TRUE(g.hasFlag(Type::Flag::kHasObjectDunderNew));
1524 EXPECT_FALSE(g.hasFlag(Type::Flag::kHasObjectDunderHash));
1525
1526 Type h(&scope, mainModuleAt(runtime_, "H"));
1527 EXPECT_TRUE(h.hasFlag(Type::Flag::kHasDunderBool));
1528 EXPECT_FALSE(h.hasFlag(Type::Flag::kHasDunderLen));
1529
1530 Type i(&scope, mainModuleAt(runtime_, "I"));
1531 EXPECT_FALSE(i.hasFlag(Type::Flag::kHasDunderBool));
1532 EXPECT_TRUE(i.hasFlag(Type::Flag::kHasDunderLen));
1533
1534 Type j(&scope, mainModuleAt(runtime_, "J"));
1535 EXPECT_FALSE(j.hasFlag(Type::Flag::kHasObjectDunderClass));
1536
1537 Type k(&scope, mainModuleAt(runtime_, "K"));
1538 EXPECT_TRUE(k.hasFlag(Type::Flag::kHasDunderGet));
1539 EXPECT_FALSE(k.hasFlag(Type::Flag::kHasDunderSet));
1540 EXPECT_FALSE(k.hasFlag(Type::Flag::kHasDunderDelete));
1541 EXPECT_FALSE(typeIsDataDescriptor(*k));
1542 EXPECT_TRUE(typeIsNonDataDescriptor(*k));
1543
1544 Type l(&scope, mainModuleAt(runtime_, "L"));
1545 EXPECT_FALSE(l.hasFlag(Type::Flag::kHasDunderGet));
1546 EXPECT_TRUE(l.hasFlag(Type::Flag::kHasDunderSet));
1547 EXPECT_FALSE(l.hasFlag(Type::Flag::kHasDunderDelete));
1548 EXPECT_TRUE(typeIsDataDescriptor(*l));
1549 EXPECT_FALSE(typeIsNonDataDescriptor(*l));
1550
1551 Type m(&scope, mainModuleAt(runtime_, "M"));
1552 EXPECT_FALSE(m.hasFlag(Type::Flag::kHasDunderGet));
1553 EXPECT_FALSE(m.hasFlag(Type::Flag::kHasDunderSet));
1554 EXPECT_TRUE(m.hasFlag(Type::Flag::kHasDunderDelete));
1555 EXPECT_TRUE(typeIsDataDescriptor(*m));
1556 EXPECT_FALSE(typeIsNonDataDescriptor(*m));
1557
1558 Type n(&scope, mainModuleAt(runtime_, "N"));
1559 EXPECT_FALSE(n.hasFlag(Type::Flag::kHasObjectDunderEq));
1560
1561 Type sub_n(&scope, mainModuleAt(runtime_, "SubN"));
1562 EXPECT_FALSE(sub_n.hasFlag(Type::Flag::kHasObjectDunderEq));
1563
1564 Type str(&scope, mainModuleAt(runtime_, "Str"));
1565 EXPECT_TRUE(str.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1566 EXPECT_FALSE(str.hasFlag(Type::Flag::kHasObjectDunderNew));
1567 EXPECT_FALSE(str.hasFlag(Type::Flag::kHasObjectDunderHash));
1568 EXPECT_TRUE(str.hasFlag(Type::Flag::kHasStrDunderHash));
1569
1570 Type str2(&scope, mainModuleAt(runtime_, "Str2"));
1571 EXPECT_TRUE(str2.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1572 EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasObjectDunderNew));
1573 EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasObjectDunderHash));
1574 EXPECT_FALSE(str2.hasFlag(Type::Flag::kHasStrDunderHash));
1575}
1576
1577TEST_F(TypeBuiltinsTest, AttributeTypeFlagsPropagateThroughTypeHierarchy) {
1578 HandleScope scope(thread_);
1579 ASSERT_FALSE(runFromCStr(runtime_, R"(
1580class A:
1581 pass
1582
1583class B(A):
1584 def __getattribute__(self, name): return name
1585
1586class C(B):
1587 pass
1588
1589class X:
1590 pass
1591
1592class D(X, C):
1593 pass
1594)")
1595 .isError());
1596 Type a(&scope, mainModuleAt(runtime_, "A"));
1597 EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1598 Type b(&scope, mainModuleAt(runtime_, "B"));
1599 EXPECT_FALSE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1600 Type c(&scope, mainModuleAt(runtime_, "C"));
1601 EXPECT_FALSE(c.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1602 Type d(&scope, mainModuleAt(runtime_, "D"));
1603 EXPECT_FALSE(d.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1604}
1605
1606TEST_F(TypeBuiltinsTest,
1607 AttributeTypeFlagsForTypesWithMetaclassWithDefaultMro) {
1608 HandleScope scope(thread_);
1609 ASSERT_FALSE(runFromCStr(runtime_, R"(
1610class MetaWithDefaultMro(type):
1611 def foo(self): return 500
1612
1613class A(metaclass=MetaWithDefaultMro):
1614 pass
1615
1616class B(A):
1617 pass
1618)")
1619 .isError());
1620 Type a(&scope, mainModuleAt(runtime_, "A"));
1621 EXPECT_FALSE(a.hasFlag(Type::Flag::kHasCustomMro));
1622 EXPECT_TRUE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1623 Type b(&scope, mainModuleAt(runtime_, "B"));
1624 EXPECT_TRUE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1625}
1626
1627TEST_F(TypeBuiltinsTest, AttributeTypeFlagsForTypesWithMetaclassWithCustomMro) {
1628 HandleScope scope(thread_);
1629 ASSERT_FALSE(runFromCStr(runtime_, R"(
1630class MetaWithCustomMro(type):
1631 def mro(self):
1632 return (self,)
1633
1634class A(metaclass=MetaWithCustomMro):
1635 pass
1636
1637class B(A):
1638 pass
1639)")
1640 .isError());
1641 Type a(&scope, mainModuleAt(runtime_, "A"));
1642 EXPECT_TRUE(a.hasFlag(Type::Flag::kHasCustomMro));
1643 EXPECT_FALSE(a.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1644 Type b(&scope, mainModuleAt(runtime_, "B"));
1645 EXPECT_FALSE(b.hasFlag(Type::Flag::kHasObjectDunderGetattribute));
1646}
1647
1648} // namespace testing
1649} // namespace py