Skip to content

Commit f078619

Browse files
SC llvm teamSC llvm team
SC llvm team
authored and
SC llvm team
committed
Merged main:6479218932117ab36cba578bb458ee7c0fb4bf0a into amd-gfx:0667a16be0e8
Local branch amd-gfx 0667a16 Merged main:2cd19df792056bbac38ed64c028e335d0c7ef05d into amd-gfx:a9792f68f51a Remote branch main 6479218 Fix -Werror build
2 parents 0667a16 + 6479218 commit f078619

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2641
-1021
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
/clang/www/cxx_dr_status.html @Endilll
3333
/clang/www/make_cxx_dr_status @Endilll
3434

35+
clang/lib/AST/Interp/ @tbaederr
36+
clang/test/AST/Interp/ @tbaederr
37+
3538
/lldb/ @JDevlieghere
3639

3740
# MLIR Interfaces.

clang/docs/ReleaseNotes.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,21 @@ Attribute Changes in Clang
201201
and each must be a positive integer when provided. The parameter ``x`` is required, while ``y`` and
202202
``z`` are optional with default value of 1.
203203

204+
- The ``_Nullable`` and ``_Nonnull`` family of type attributes can now apply
205+
to certain C++ class types, such as smart pointers:
206+
``void useObject(std::unique_ptr<Object> _Nonnull obj);``.
207+
208+
This works for standard library types including ``unique_ptr``, ``shared_ptr``
209+
and ``function``. See `the attribute reference
210+
documentation <https://llvm.org/docs/AttributeReference.html#nullability-attributes>`_
211+
for the full list.
212+
213+
- The ``_Nullable`` attribute can be applied to C++ class declarations:
214+
``template <class T> class _Nullable MySmartPointer {};``.
215+
216+
This allows the ``_Nullable`` and ``_Nonnull` family of type attributes to
217+
apply to this class.
218+
204219
Improvements to Clang's diagnostics
205220
-----------------------------------
206221
- Clang now applies syntax highlighting to the code snippets it

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2178,9 +2178,10 @@ def TypeNonNull : TypeAttr {
21782178
let Documentation = [TypeNonNullDocs];
21792179
}
21802180

2181-
def TypeNullable : TypeAttr {
2181+
def TypeNullable : DeclOrTypeAttr {
21822182
let Spellings = [CustomKeyword<"_Nullable">];
21832183
let Documentation = [TypeNullableDocs];
2184+
// let Subjects = SubjectList<[CXXRecord], ErrorDiag>;
21842185
}
21852186

21862187
def TypeNullableResult : TypeAttr {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4151,6 +4151,20 @@ non-underscored keywords. For example:
41514151
@property (assign, nullable) NSView *superview;
41524152
@property (readonly, nonnull) NSArray *subviews;
41534153
@end
4154+
4155+
As well as built-in pointer types, the nullability attributes can be attached
4156+
to C++ classes marked with the ``_Nullable`` attribute.
4157+
4158+
The following C++ standard library types are considered nullable:
4159+
``unique_ptr``, ``shared_ptr``, ``auto_ptr``, ``exception_ptr``, ``function``,
4160+
``move_only_function`` and ``coroutine_handle``.
4161+
4162+
Types should be marked nullable only where the type itself leaves nullability
4163+
ambiguous. For example, ``std::optional`` is not marked ``_Nullable``, because
4164+
``optional<int> _Nullable`` is redundant and ``optional<int> _Nonnull`` is
4165+
not a useful type. ``std::weak_ptr`` is not nullable, because its nullability
4166+
can change with no visible modification, so static annotation is unlikely to be
4167+
unhelpful.
41544168
}];
41554169
}
41564170

@@ -4185,6 +4199,17 @@ The ``_Nullable`` nullability qualifier indicates that a value of the
41854199
int fetch_or_zero(int * _Nullable ptr);
41864200

41874201
a caller of ``fetch_or_zero`` can provide null.
4202+
4203+
The ``_Nullable`` attribute on classes indicates that the given class can
4204+
represent null values, and so the ``_Nullable``, ``_Nonnull`` etc qualifiers
4205+
make sense for this type. For example:
4206+
4207+
.. code-block:: c
4208+
4209+
class _Nullable ArenaPointer { ... };
4210+
4211+
ArenaPointer _Nonnull x = ...;
4212+
ArenaPointer _Nullable y = nullptr;
41884213
}];
41894214
}
41904215

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ EXTENSION(define_target_os_macros,
9494
FEATURE(enumerator_attributes, true)
9595
FEATURE(nullability, true)
9696
FEATURE(nullability_on_arrays, true)
97+
FEATURE(nullability_on_classes, true)
9798
FEATURE(nullability_nullable_result, true)
9899
FEATURE(memory_sanitizer,
99100
LangOpts.Sanitize.hasOneOf(SanitizerKind::Memory |

clang/include/clang/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,6 +3014,7 @@ class Parser : public CodeCompletionHandler {
30143014
void DiagnoseAndSkipExtendedMicrosoftTypeAttributes();
30153015
SourceLocation SkipExtendedMicrosoftTypeAttributes();
30163016
void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs);
3017+
void ParseNullabilityClassAttributes(ParsedAttributes &attrs);
30173018
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
30183019
void ParseOpenCLKernelAttributes(ParsedAttributes &attrs);
30193020
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,9 @@ class Sema final {
16551655
/// Add [[gsl::Pointer]] attributes for std:: types.
16561656
void inferGslPointerAttribute(TypedefNameDecl *TD);
16571657

1658+
/// Add _Nullable attributes for std:: types.
1659+
void inferNullableClassAttribute(CXXRecordDecl *CRD);
1660+
16581661
enum PragmaOptionsAlignKind {
16591662
POAK_Native, // #pragma options align=native
16601663
POAK_Natural, // #pragma options align=natural

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -685,11 +685,17 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
685685
if (!this->emitSetLocal(PT_Ptr, ResultOffset, E))
686686
return false;
687687
}
688+
QualType LHSType = LHS->getType();
689+
if (const auto *AT = LHSType->getAs<AtomicType>())
690+
LHSType = AT->getValueType();
691+
QualType RHSType = RHS->getType();
692+
if (const auto *AT = RHSType->getAs<AtomicType>())
693+
RHSType = AT->getValueType();
688694

689695
// Evaluate LHS and save value to LHSOffset.
690696
bool LHSIsComplex;
691697
unsigned LHSOffset;
692-
if (LHS->getType()->isAnyComplexType()) {
698+
if (LHSType->isAnyComplexType()) {
693699
LHSIsComplex = true;
694700
LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
695701
if (!this->visit(LHS))
@@ -698,7 +704,7 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
698704
return false;
699705
} else {
700706
LHSIsComplex = false;
701-
PrimType LHST = classifyPrim(LHS->getType());
707+
PrimType LHST = classifyPrim(LHSType);
702708
LHSOffset = this->allocateLocalPrimitive(LHS, LHST, true, false);
703709
if (!this->visit(LHS))
704710
return false;
@@ -709,7 +715,7 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
709715
// Same with RHS.
710716
bool RHSIsComplex;
711717
unsigned RHSOffset;
712-
if (RHS->getType()->isAnyComplexType()) {
718+
if (RHSType->isAnyComplexType()) {
713719
RHSIsComplex = true;
714720
RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
715721
if (!this->visit(RHS))
@@ -718,7 +724,7 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
718724
return false;
719725
} else {
720726
RHSIsComplex = false;
721-
PrimType RHST = classifyPrim(RHS->getType());
727+
PrimType RHST = classifyPrim(RHSType);
722728
RHSOffset = this->allocateLocalPrimitive(RHS, RHST, true, false);
723729
if (!this->visit(RHS))
724730
return false;

clang/lib/AST/Interp/Interp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
168168
const auto *Op = cast<BinaryOperator>(S.Current->getExpr(OpPC));
169169
S.FFDiag(Op, diag::note_expr_divide_by_zero)
170170
<< Op->getRHS()->getSourceRange();
171-
return false;
171+
if constexpr (!std::is_same_v<T, Floating>)
172+
return false;
172173
}
173174

174175
if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,12 @@ static bool interp__builtin_rotate(InterpState &S, CodePtr OpPC,
533533
const InterpFrame *Frame,
534534
const Function *Func, const CallExpr *Call,
535535
bool Right) {
536-
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
537-
assert(ArgT == *S.getContext().classify(Call->getArg(1)->getType()));
536+
PrimType AmountT = *S.getContext().classify(Call->getArg(1)->getType());
537+
PrimType ValueT = *S.getContext().classify(Call->getArg(0)->getType());
538538

539-
APSInt Amount = peekToAPSInt(S.Stk, ArgT);
540-
APSInt Value = peekToAPSInt(S.Stk, ArgT, align(primSize(ArgT)) * 2);
539+
APSInt Amount = peekToAPSInt(S.Stk, AmountT);
540+
APSInt Value = peekToAPSInt(
541+
S.Stk, ValueT, align(primSize(AmountT)) + align(primSize(ValueT)));
541542

542543
APSInt Result;
543544
if (Right)

clang/lib/AST/Type.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,16 +4558,15 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
45584558
case Type::Auto:
45594559
return ResultIfUnknown;
45604560

4561-
// Dependent template specializations can instantiate to pointer
4562-
// types unless they're known to be specializations of a class
4563-
// template.
4561+
// Dependent template specializations could instantiate to pointer types.
45644562
case Type::TemplateSpecialization:
4565-
if (TemplateDecl *templateDecl
4566-
= cast<TemplateSpecializationType>(type.getTypePtr())
4567-
->getTemplateName().getAsTemplateDecl()) {
4568-
if (isa<ClassTemplateDecl>(templateDecl))
4569-
return false;
4570-
}
4563+
// If it's a known class template, we can already check if it's nullable.
4564+
if (TemplateDecl *templateDecl =
4565+
cast<TemplateSpecializationType>(type.getTypePtr())
4566+
->getTemplateName()
4567+
.getAsTemplateDecl())
4568+
if (auto *CTD = dyn_cast<ClassTemplateDecl>(templateDecl))
4569+
return CTD->getTemplatedDecl()->hasAttr<TypeNullableAttr>();
45714570
return ResultIfUnknown;
45724571

45734572
case Type::Builtin:
@@ -4624,6 +4623,17 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
46244623
}
46254624
llvm_unreachable("unknown builtin type");
46264625

4626+
case Type::Record: {
4627+
const RecordDecl *RD = cast<RecordType>(type)->getDecl();
4628+
// For template specializations, look only at primary template attributes.
4629+
// This is a consistent regardless of whether the instantiation is known.
4630+
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD))
4631+
return CTSD->getSpecializedTemplate()
4632+
->getTemplatedDecl()
4633+
->hasAttr<TypeNullableAttr>();
4634+
return RD->hasAttr<TypeNullableAttr>();
4635+
}
4636+
46274637
// Non-pointer types.
46284638
case Type::Complex:
46294639
case Type::LValueReference:
@@ -4641,7 +4651,6 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
46414651
case Type::DependentAddressSpace:
46424652
case Type::FunctionProto:
46434653
case Type::FunctionNoProto:
4644-
case Type::Record:
46454654
case Type::DeducedTemplateSpecialization:
46464655
case Type::Enum:
46474656
case Type::InjectedClassName:

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4372,7 +4372,8 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
43724372
NNAttr = getNonNullAttr(AC.getDecl(), PVD, ArgType, ArgNo);
43734373

43744374
bool CanCheckNullability = false;
4375-
if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD) {
4375+
if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD &&
4376+
!PVD->getType()->isRecordType()) {
43764377
auto Nullability = PVD->getType()->getNullability();
43774378
CanCheckNullability = Nullability &&
43784379
*Nullability == NullabilityKind::NonNull &&

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
979979
// return value. Initialize the flag to 'true' and refine it in EmitParmDecl.
980980
if (SanOpts.has(SanitizerKind::NullabilityReturn)) {
981981
auto Nullability = FnRetTy->getNullability();
982-
if (Nullability && *Nullability == NullabilityKind::NonNull) {
982+
if (Nullability && *Nullability == NullabilityKind::NonNull &&
983+
!FnRetTy->isRecordType()) {
983984
if (!(SanOpts.has(SanitizerKind::ReturnsNonnullAttribute) &&
984985
CurCodeDecl && CurCodeDecl->getAttr<ReturnsNonNullAttr>()))
985986
RetValNullabilityPrecondition =

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,15 @@ void Parser::ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs) {
14941494
}
14951495
}
14961496

1497+
void Parser::ParseNullabilityClassAttributes(ParsedAttributes &attrs) {
1498+
while (Tok.is(tok::kw__Nullable)) {
1499+
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
1500+
auto Kind = Tok.getKind();
1501+
SourceLocation AttrNameLoc = ConsumeToken();
1502+
attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, Kind);
1503+
}
1504+
}
1505+
14971506
/// Determine whether the following tokens are valid after a type-specifier
14981507
/// which could be a standalone declaration. This will conservatively return
14991508
/// true if there's any doubt, and is appropriate for insert-';' fixits.
@@ -1675,15 +1684,21 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
16751684

16761685
ParsedAttributes attrs(AttrFactory);
16771686
// If attributes exist after tag, parse them.
1678-
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1679-
1680-
// Parse inheritance specifiers.
1681-
if (Tok.isOneOf(tok::kw___single_inheritance, tok::kw___multiple_inheritance,
1682-
tok::kw___virtual_inheritance))
1683-
ParseMicrosoftInheritanceClassAttributes(attrs);
1684-
1685-
// Allow attributes to precede or succeed the inheritance specifiers.
1686-
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1687+
for (;;) {
1688+
MaybeParseAttributes(PAKM_CXX11 | PAKM_Declspec | PAKM_GNU, attrs);
1689+
// Parse inheritance specifiers.
1690+
if (Tok.isOneOf(tok::kw___single_inheritance,
1691+
tok::kw___multiple_inheritance,
1692+
tok::kw___virtual_inheritance)) {
1693+
ParseMicrosoftInheritanceClassAttributes(attrs);
1694+
continue;
1695+
}
1696+
if (Tok.is(tok::kw__Nullable)) {
1697+
ParseNullabilityClassAttributes(attrs);
1698+
continue;
1699+
}
1700+
break;
1701+
}
16871702

16881703
// Source location used by FIXIT to insert misplaced
16891704
// C++11 attributes

clang/lib/Sema/SemaAttr.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,18 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
215215
inferGslPointerAttribute(Record, Record);
216216
}
217217

218+
void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
219+
static llvm::StringSet<> Nullable{
220+
"auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr",
221+
"coroutine_handle", "function", "move_only_function",
222+
};
223+
224+
if (CRD->isInStdNamespace() && Nullable.count(CRD->getName()) &&
225+
!CRD->hasAttr<TypeNullableAttr>())
226+
for (Decl *Redecl : CRD->redecls())
227+
Redecl->addAttr(TypeNullableAttr::CreateImplicit(Context));
228+
}
229+
218230
void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
219231
SourceLocation PragmaLoc) {
220232
PragmaMsStackAction Action = Sema::PSK_Reset;

clang/lib/Sema/SemaChecking.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "clang/AST/ExprObjC.h"
2828
#include "clang/AST/ExprOpenMP.h"
2929
#include "clang/AST/FormatString.h"
30+
#include "clang/AST/IgnoreExpr.h"
3031
#include "clang/AST/NSAPI.h"
3132
#include "clang/AST/NonTrivialTypeVisitor.h"
3233
#include "clang/AST/OperationKinds.h"
@@ -7316,6 +7317,14 @@ bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember,
73167317
///
73177318
/// Returns true if the value evaluates to null.
73187319
static bool CheckNonNullExpr(Sema &S, const Expr *Expr) {
7320+
// Treat (smart) pointers constructed from nullptr as null, whether we can
7321+
// const-evaluate them or not.
7322+
// This must happen first: the smart pointer expr might have _Nonnull type!
7323+
if (isa<CXXNullPtrLiteralExpr>(
7324+
IgnoreExprNodes(Expr, IgnoreImplicitAsWrittenSingleStep,
7325+
IgnoreElidableImplicitConstructorSingleStep)))
7326+
return true;
7327+
73197328
// If the expression has non-null type, it doesn't evaluate to null.
73207329
if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability()) {
73217330
if (*nullability == NullabilityKind::NonNull)

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18254,8 +18254,10 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
1825418254
if (PrevDecl)
1825518255
mergeDeclAttributes(New, PrevDecl);
1825618256

18257-
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New))
18257+
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New)) {
1825818258
inferGslOwnerPointerAttribute(CXXRD);
18259+
inferNullableClassAttribute(CXXRD);
18260+
}
1825918261

1826018262
// If there's a #pragma GCC visibility in scope, set the visibility of this
1826118263
// record.

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5976,6 +5976,20 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D,
59765976
D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident));
59775977
}
59785978

5979+
static void handleNullableTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
5980+
if (AL.isUsedAsTypeAttr())
5981+
return;
5982+
5983+
if (auto *CRD = dyn_cast<CXXRecordDecl>(D);
5984+
!CRD || !(CRD->isClass() || CRD->isStruct())) {
5985+
S.Diag(AL.getRange().getBegin(), diag::err_attribute_wrong_decl_type_str)
5986+
<< AL << AL.isRegularKeywordAttribute() << "classes";
5987+
return;
5988+
}
5989+
5990+
handleSimpleAttribute<TypeNullableAttr>(S, D, AL);
5991+
}
5992+
59795993
static void handlePreferredTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
59805994
if (!AL.hasParsedType()) {
59815995
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
@@ -9945,6 +9959,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
99459959
case ParsedAttr::AT_UsingIfExists:
99469960
handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL);
99479961
break;
9962+
9963+
case ParsedAttr::AT_TypeNullable:
9964+
handleNullableTypeAttr(S, D, AL);
9965+
break;
99489966
}
99499967
}
99509968

clang/lib/Sema/SemaInit.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7075,6 +7075,11 @@ PerformConstructorInitialization(Sema &S,
70757075
hasCopyOrMoveCtorParam(S.Context,
70767076
getConstructorInfo(Step.Function.FoundDecl));
70777077

7078+
// A smart pointer constructed from a nullable pointer is nullable.
7079+
if (NumArgs == 1 && !Kind.isExplicitCast())
7080+
S.diagnoseNullableToNonnullConversion(
7081+
Entity.getType(), Args.front()->getType(), Kind.getLocation());
7082+
70787083
// Determine the arguments required to actually perform the constructor
70797084
// call.
70807085
if (S.CompleteConstructorCall(Constructor, Step.Type, Args, Loc,

0 commit comments

Comments
 (0)