Skip to content

Commit 049aac1

Browse files
authored
Support class types. (shader-slang#2321)
* Support `class` types. * Ignore class-keyword test * Fix codereview comments and warnings. Co-authored-by: Yong He <yhe@nvidia.com>
1 parent a677566 commit 049aac1

25 files changed

+412
-74
lines changed

build/visual-studio/slang/slang.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ IF EXIST ..\..\..\external\slang-binaries\bin\windows-aarch64\slang-glslang.dll\
411411
<ClInclude Include="..\..\..\source\slang\slang-ir-synthesize-active-mask.h" />
412412
<ClInclude Include="..\..\..\source\slang\slang-ir-type-set.h" />
413413
<ClInclude Include="..\..\..\source\slang\slang-ir-union.h" />
414+
<ClInclude Include="..\..\..\source\slang\slang-ir-util.h" />
414415
<ClInclude Include="..\..\..\source\slang\slang-ir-validate.h" />
415416
<ClInclude Include="..\..\..\source\slang\slang-ir-witness-table-wrapper.h" />
416417
<ClInclude Include="..\..\..\source\slang\slang-ir-wrap-structured-buffers.h" />
@@ -569,6 +570,7 @@ IF EXIST ..\..\..\external\slang-binaries\bin\windows-aarch64\slang-glslang.dll\
569570
<ClCompile Include="..\..\..\source\slang\slang-ir-synthesize-active-mask.cpp" />
570571
<ClCompile Include="..\..\..\source\slang\slang-ir-type-set.cpp" />
571572
<ClCompile Include="..\..\..\source\slang\slang-ir-union.cpp" />
573+
<ClCompile Include="..\..\..\source\slang\slang-ir-util.cpp" />
572574
<ClCompile Include="..\..\..\source\slang\slang-ir-validate.cpp" />
573575
<ClCompile Include="..\..\..\source\slang\slang-ir-witness-table-wrapper.cpp" />
574576
<ClCompile Include="..\..\..\source\slang\slang-ir-wrap-structured-buffers.cpp" />

build/visual-studio/slang/slang.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@
330330
<ClInclude Include="..\..\..\source\slang\slang-ir-union.h">
331331
<Filter>Header Files</Filter>
332332
</ClInclude>
333+
<ClInclude Include="..\..\..\source\slang\slang-ir-util.h">
334+
<Filter>Header Files</Filter>
335+
</ClInclude>
333336
<ClInclude Include="..\..\..\source\slang\slang-ir-validate.h">
334337
<Filter>Header Files</Filter>
335338
</ClInclude>
@@ -800,6 +803,9 @@
800803
<ClCompile Include="..\..\..\source\slang\slang-ir-union.cpp">
801804
<Filter>Source Files</Filter>
802805
</ClCompile>
806+
<ClCompile Include="..\..\..\source\slang\slang-ir-util.cpp">
807+
<Filter>Source Files</Filter>
808+
</ClCompile>
803809
<ClCompile Include="..\..\..\source\slang\slang-ir-validate.cpp">
804810
<Filter>Source Files</Filter>
805811
</ClCompile>

source/slang/slang-ast-expr.h

+5
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ class TryExpr : public Expr
181181
Scope* scope = nullptr;
182182
};
183183

184+
class NewExpr : public InvokeExpr
185+
{
186+
SLANG_AST_CLASS(NewExpr)
187+
};
188+
184189
class OperatorExpr: public InvokeExpr
185190
{
186191
SLANG_AST_CLASS(OperatorExpr)

source/slang/slang-check-decl.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ namespace Slang
9090

9191
void visitStructDecl(StructDecl* decl);
9292

93+
void visitClassDecl(ClassDecl* decl);
94+
9395
/// Get the type of the storage accessed by an accessor.
9496
///
9597
/// The type of storage is determined by the parent declaration.
@@ -149,6 +151,8 @@ namespace Slang
149151

150152
void visitStructDecl(StructDecl* decl);
151153

154+
void visitClassDecl(ClassDecl* decl);
155+
152156
void visitEnumDecl(EnumDecl* decl);
153157

154158
/// Validate that the target type of an extension `decl` is valid.
@@ -1065,6 +1069,11 @@ namespace Slang
10651069
}
10661070
}
10671071

1072+
void SemanticsDeclHeaderVisitor::visitClassDecl(ClassDecl* classDecl)
1073+
{
1074+
SLANG_UNUSED(classDecl);
1075+
}
1076+
10681077
void SemanticsDeclBodyVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
10691078
{
10701079
if (auto initExpr = varDecl->initExpr)
@@ -3370,6 +3379,75 @@ namespace Slang
33703379
}
33713380
}
33723381

3382+
void SemanticsDeclBasesVisitor::visitClassDecl(ClassDecl* decl)
3383+
{
3384+
// A `class` type can only inherit from `class` or `interface` types.
3385+
//
3386+
// Furthermore, only the first inheritance clause (in source
3387+
// order) is allowed to declare a base `class` type.
3388+
//
3389+
Index inheritanceClauseCounter = 0;
3390+
for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
3391+
{
3392+
Index inheritanceClauseIndex = inheritanceClauseCounter++;
3393+
3394+
ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
3395+
auto baseType = inheritanceDecl->base.type;
3396+
3397+
// It is possible that there was an error in checking the base type
3398+
// expression, and in such a case we shouldn't emit a cascading error.
3399+
//
3400+
if (auto baseErrorType = as<ErrorType>(baseType))
3401+
{
3402+
continue;
3403+
}
3404+
3405+
auto baseDeclRefType = as<DeclRefType>(baseType);
3406+
if (!baseDeclRefType)
3407+
{
3408+
getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfClassMustBeClassOrInterface, decl, baseType);
3409+
continue;
3410+
}
3411+
3412+
auto baseDeclRef = baseDeclRefType->declRef;
3413+
if (auto baseInterfaceDeclRef = baseDeclRef.as<InterfaceDecl>())
3414+
{
3415+
}
3416+
else if (auto baseStructDeclRef = baseDeclRef.as<ClassDecl>())
3417+
{
3418+
// To simplify the task of reading and maintaining code,
3419+
// we require that when a `class` inherits from another
3420+
// `class`, the base `class` is the first item in
3421+
// the list of bases (before any interfaces).
3422+
//
3423+
// This constraint also has the secondary effect of restricting
3424+
// it so that a `struct` cannot multiply inherit from other
3425+
// `struct` types.
3426+
//
3427+
if (inheritanceClauseIndex != 0)
3428+
{
3429+
getSink()->diagnose(inheritanceDecl, Diagnostics::baseClassMustBeListedFirst, decl, baseType);
3430+
}
3431+
}
3432+
else
3433+
{
3434+
getSink()->diagnose(inheritanceDecl, Diagnostics::baseOfClassMustBeClassOrInterface, decl, baseType);
3435+
continue;
3436+
}
3437+
3438+
// TODO: At this point we have the `baseDeclRef`
3439+
// and could use it to perform further validity checks,
3440+
// and/or to build up a more refined representation of
3441+
// the inheritance graph for this type (e.g., a "class
3442+
// precedence list").
3443+
//
3444+
// E.g., we can/should check that we aren't introducing
3445+
// a circular inheritance relationship.
3446+
3447+
_validateCrossModuleInheritance(decl, inheritanceDecl);
3448+
}
3449+
}
3450+
33733451
bool SemanticsVisitor::isIntegerBaseType(BaseType baseType)
33743452
{
33753453
return (BaseTypeInfo::getInfo(baseType).flags & BaseTypeInfo::Flag::Integer) != 0;

source/slang/slang-check-expr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1675,7 +1675,7 @@ namespace Slang
16751675

16761676
Expr* SemanticsExprVisitor::visitTryExpr(TryExpr* expr)
16771677
{
1678-
auto prevTryClauseType = expr->tryClauseType;
1678+
auto prevTryClauseType = m_enclosingTryClauseType;
16791679
m_enclosingTryClauseType = expr->tryClauseType;
16801680
expr->base = CheckTerm(expr->base);
16811681
m_enclosingTryClauseType = prevTryClauseType;

source/slang/slang-check-impl.h

+4
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,10 @@ namespace Slang
13871387
// count the number of parameters required/allowed for a generic
13881388
ParamCounts CountParameters(DeclRef<GenericDecl> genericRef);
13891389

1390+
bool TryCheckOverloadCandidateClassNewMatchUp(
1391+
OverloadResolveContext& context,
1392+
OverloadCandidate const& candidate);
1393+
13901394
bool TryCheckOverloadCandidateArity(
13911395
OverloadResolveContext& context,
13921396
OverloadCandidate const& candidate);

source/slang/slang-check-overload.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,40 @@ namespace Slang
6060
return counts;
6161
}
6262

63+
bool SemanticsVisitor::TryCheckOverloadCandidateClassNewMatchUp(OverloadResolveContext& context, OverloadCandidate const& candidate)
64+
{
65+
// Check that a constructor call to a class type must be in a `new` expr, and a `new` expr
66+
// is only used to construct a class.
67+
bool isClassType = false;
68+
bool isNewExpr = false;
69+
if (auto ctorDeclRef = candidate.item.declRef.as<ConstructorDecl>())
70+
{
71+
if (auto resultType = as<DeclRefType>(candidate.resultType))
72+
{
73+
if (resultType->declRef.as<ClassDecl>())
74+
{
75+
isClassType = true;
76+
}
77+
}
78+
}
79+
if (as<NewExpr>(context.originalExpr))
80+
{
81+
isNewExpr = true;
82+
}
83+
84+
if (isNewExpr && !isClassType)
85+
{
86+
getSink()->diagnose(context.originalExpr, Diagnostics::newCanOnlyBeUsedToInitializeAClass);
87+
return false;
88+
}
89+
if (!isNewExpr && isClassType)
90+
{
91+
getSink()->diagnose(context.originalExpr, Diagnostics::classCanOnlyBeInitializedWithNew);
92+
return false;
93+
}
94+
return true;
95+
}
96+
6397
bool SemanticsVisitor::TryCheckOverloadCandidateArity(
6498
OverloadResolveContext& context,
6599
OverloadCandidate const& candidate)
@@ -572,6 +606,9 @@ namespace Slang
572606

573607
context.mode = OverloadResolveContext::Mode::ForReal;
574608

609+
if (!TryCheckOverloadCandidateClassNewMatchUp(context, candidate))
610+
goto error;
611+
575612
if (!TryCheckOverloadCandidateArity(context, candidate))
576613
goto error;
577614

source/slang/slang-diagnostic-defs.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ DIAGNOSTIC(30043, Error, getStringHashRequiresStringLiteral, "getStringHash para
277277
DIAGNOSTIC(30060, Error, expectedAType, "expected a type, got a '$0'")
278278
DIAGNOSTIC(30061, Error, expectedANamespace, "expected a namespace, got a '$0'")
279279

280+
DIAGNOSTIC(30065, Error, newCanOnlyBeUsedToInitializeAClass, "`new` can only be used to initialize a class")
281+
DIAGNOSTIC(30066, Error, classCanOnlyBeInitializedWithNew, "a class can only be initialized by a `new` clause")
282+
280283
DIAGNOSTIC(30100, Error, staticRefToNonStaticMember, "type '$0' cannot be used to refer to non-static member '$1'")
281284

282285
DIAGNOSTIC(30200, Error, redeclaration, "declaration of '$0' conflicts with existing declaration")
@@ -357,14 +360,16 @@ DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'in
357360
DIAGNOSTIC(30810, Error, baseOfInterfaceMustBeInterface, "interface '$0' cannot inherit from non-interface type '$1'")
358361
DIAGNOSTIC(30811, Error, baseOfStructMustBeStructOrInterface, "struct '$0' cannot inherit from type '$1' that is neither a struct nor an interface")
359362
DIAGNOSTIC(30812, Error, baseOfEnumMustBeIntegerOrInterface, "enum '$0' cannot inherit from type '$1' that is neither an interface not a builtin integer type")
360-
DIAGNOSTIC(30810, Error, baseOfExtensionMustBeInterface, "extension cannot inherit from non-interface type '$1'")
363+
DIAGNOSTIC(30813, Error, baseOfExtensionMustBeInterface, "extension cannot inherit from non-interface type '$1'")
364+
DIAGNOSTIC(30814, Error, baseOfClassMustBeClassOrInterface, "class '$0' cannot inherit from type '$1' that is neither a class nor an interface")
361365

362366
DIAGNOSTIC(30820, Error, baseStructMustBeListedFirst, "a struct type may only inherit from one other struct type, and that type must appear first in the list of bases")
363367
DIAGNOSTIC(30821, Error, tagTypeMustBeListedFirst, "an unum type may only have a single tag type, and that type must be listed first in the list of bases")
368+
DIAGNOSTIC(30822, Error, baseClassMustBeListedFirst, "a class type may only inherit from one other class type, and that type must appear first in the list of bases")
364369

365-
DIAGNOSTIC(30820, Error, cannotInheritFromExplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' marked 'sealed' in module '$1'")
366-
DIAGNOSTIC(30821, Error, cannotInheritFromImplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' in module '$1' because it is implicitly 'sealed'; mark the base type 'open' to allow inheritance across modules")
367-
DIAGNOSTIC(30822, Error, invalidTypeForInheritance, "type '$0' cannot be used for inheritance")
370+
DIAGNOSTIC(30830, Error, cannotInheritFromExplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' marked 'sealed' in module '$1'")
371+
DIAGNOSTIC(30831, Error, cannotInheritFromImplicitlySealedDeclarationInAnotherModule, "cannot inherit from type '$0' in module '$1' because it is implicitly 'sealed'; mark the base type 'open' to allow inheritance across modules")
372+
DIAGNOSTIC(30832, Error, invalidTypeForInheritance, "type '$0' cannot be used for inheritance")
368373

369374
DIAGNOSTIC(30850, Error, invalidExtensionOnType, "type '$0' cannot be extended. `extension` can only be used to extend a nominal type.")
370375

source/slang/slang-emit-c-like.cpp

+57-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "slang-ir-specialize-resources.h"
1616
#include "slang-ir-ssa.h"
1717
#include "slang-ir-union.h"
18+
#include "slang-ir-util.h"
1819
#include "slang-ir-validate.h"
1920
#include "slang-legalize-types.h"
2021
#include "slang-lower-to-ir.h"
@@ -28,7 +29,6 @@
2829

2930
#include "slang-emit-source-writer.h"
3031
#include "slang-mangled-lexer.h"
31-
3232
#include <assert.h>
3333

3434
namespace Slang {
@@ -1617,6 +1617,11 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
16171617
emitType(inst->getDataType());
16181618
emitArgs(inst);
16191619
break;
1620+
case kIROp_AllocObj:
1621+
m_writer->emit("new ");
1622+
m_writer->emit(getName(inst->getDataType()));
1623+
m_writer->emit("()");
1624+
break;
16201625
case kIROp_makeUInt64:
16211626
m_writer->emit("((");
16221627
emitType(inst->getDataType());
@@ -1649,7 +1654,10 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
16491654

16501655
auto base = fieldExtract->getBase();
16511656
emitOperand(base, leftSide(outerPrec, prec));
1652-
m_writer->emit(".");
1657+
if (base->getDataType()->getOp() == kIROp_ClassType)
1658+
m_writer->emit("->");
1659+
else
1660+
m_writer->emit(".");
16531661
if(getSourceLanguage() == SourceLanguage::GLSL
16541662
&& as<IRUniformParameterGroupType>(base->getDataType()))
16551663
{
@@ -1673,7 +1681,10 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
16731681
auto innerPrec = getInfo(EmitOp::Postfix);
16741682
bool innerNeedClose = maybeEmitParens(outerPrec, innerPrec);
16751683
auto base = ii->getBase();
1676-
emitOperand(base, leftSide(outerPrec, innerPrec));
1684+
if (isPtrToClassType(base->getDataType()))
1685+
emitDereferenceOperand(base, leftSide(outerPrec, innerPrec));
1686+
else
1687+
emitOperand(base, leftSide(outerPrec, innerPrec));
16771688
m_writer->emit("->");
16781689
m_writer->emit(getName(ii->getField()));
16791690
maybeCloseParens(innerNeedClose);
@@ -2771,6 +2782,46 @@ void CLikeSourceEmitter::emitStruct(IRStructType* structType)
27712782
m_writer->emit("};\n\n");
27722783
}
27732784

2785+
void CLikeSourceEmitter::emitClass(IRClassType* classType)
2786+
{
2787+
// If the selected `class` type is actually an intrinsic
2788+
// on our target, then we don't want to emit anything at all.
2789+
if (auto intrinsicDecoration = findBestTargetIntrinsicDecoration(classType))
2790+
{
2791+
return;
2792+
}
2793+
2794+
m_writer->emit("class ");
2795+
2796+
emitPostKeywordTypeAttributes(classType);
2797+
2798+
m_writer->emit(getName(classType));
2799+
m_writer->emit(" : public RefObject");
2800+
m_writer->emit("\n{\n");
2801+
m_writer->emit("public:\n");
2802+
m_writer->indent();
2803+
2804+
for (auto ff : classType->getFields())
2805+
{
2806+
auto fieldKey = ff->getKey();
2807+
auto fieldType = ff->getFieldType();
2808+
2809+
// Filter out fields with `void` type that might
2810+
// have been introduced by legalization.
2811+
if (as<IRVoidType>(fieldType))
2812+
continue;
2813+
2814+
emitInterpolationModifiers(fieldKey, fieldType, nullptr);
2815+
2816+
emitType(fieldType, getName(fieldKey));
2817+
emitSemantics(fieldKey);
2818+
m_writer->emit(";\n");
2819+
}
2820+
2821+
m_writer->dedent();
2822+
m_writer->emit("};\n\n");
2823+
}
2824+
27742825
void CLikeSourceEmitter::emitInterpolationModifiers(IRInst* varInst, IRType* valueType, IRVarLayout* layout)
27752826
{
27762827
emitInterpolationModifiersImpl(varInst, valueType, layout);
@@ -3119,7 +3170,9 @@ void CLikeSourceEmitter::emitGlobalInstImpl(IRInst* inst)
31193170
case kIROp_StructType:
31203171
emitStruct(cast<IRStructType>(inst));
31213172
break;
3122-
3173+
case kIROp_ClassType:
3174+
emitClass(cast<IRClassType>(inst));
3175+
break;
31233176
case kIROp_InterfaceType:
31243177
emitInterface(cast<IRInterfaceType>(inst));
31253178
break;

source/slang/slang-emit-c-like.h

+2
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ class CLikeSourceEmitter: public SourceEmitterBase
353353
void emitFuncDecorations(IRFunc* func) { emitFuncDecorationsImpl(func); }
354354

355355
void emitStruct(IRStructType* structType);
356+
void emitClass(IRClassType* structType);
357+
356358

357359

358360
/// Emit type attributes that should appear after, e.g., a `struct` keyword

source/slang/slang-emit-cpp.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,13 @@ SlangResult CPPSourceEmitter::calcTypeName(IRType* type, CodeGenTarget target, S
560560
out << ">";
561561
return SLANG_OK;
562562
}
563+
case kIROp_ClassType:
564+
{
565+
out << "RefPtr<";
566+
out << getName(type);
567+
out << ">";
568+
return SLANG_OK;
569+
}
563570
default:
564571
{
565572
if (isNominalOp(type->getOp()))

0 commit comments

Comments
 (0)