summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-01-24 16:04:10 -0800
committerGitHub <noreply@github.com>2019-01-24 16:04:10 -0800
commit8171a553c2523906240f5653cd1fa5c169dd89b9 (patch)
tree45240a845230a2f754be24bf16d90321fc7722a4 /source
parenteda82cbfb92d45fbbff8dfd990798605d277608a (diff)
Support "modern" declaration syntax as an option (#792)
* Support "modern" declaration syntax as an option Fixed #202 This change adds four new declaration keywords: The `let` and `var` keywords introduce immutable and mutable variables, respectively. They can only be used to declare a single variable at a time (unlike C declaration syntax), and they support inference of the variable's type from its initial-value expression. Examples: ``` let a : int = 1; // immutable with explicit type and initial-value expression let b = a + 1; // immutable, with type inferred var c : float; // mutable, with explicit type var d = b + c; // mutable, with type inferred ``` These declaration forms can be used wherever ordinary global, local, or member variable declarations appeared before. Right now they do not change rules about what is or is not considered a shader parameter. The `static` modifier should work on these forms as expected, but a `static let` variable is *not* the same as a `static const`, so an explicit `const` is still needed if you want that behavior. A `typealias` declaration introduces a named type alias, similar to `typedef`, but with more reasonable syntax. It inherits from the same AST class that `typedef` uses, so all of the code after parsing should be able to treat them as equivalent. To give a simple example: ``` // typedef int MyArray[3]; typealais MyArray = int[3]; ``` A `func` declaration introduces a function. Like `typealias` it re-uses the existing AST class, so there is no need for major changes after parsing. A `func` declaration uses a syntax similar to `let` variables for its parameters, and takes the (optional) result type in a trailing position. For example: ``` func myAdd(a: int, b: int) -> int { return a + b; } ``` If a `func` declaration leaves of the return type clause, the return type is assumed to be `void`. The main difference (beyond the trailing return type) is that the parameters of a `func`-declared function are immutable (unless they are `out`/`inout`). This change doesn't add support for declaring operator overloads with `func`, but that should be added later, and I'd like to make that the only way to declare such operations: ``` func +(left: MyType, right: MyType) -> MyType { ... } ``` The use of `:` for declaring parameter types here means that a function declared with modern syntax currently cannot include HLSL-style semantics on its parameters (or its result). We might consider introducing an `[attribute]`-based syntax for adding semantics to parameters if we think this is important, but for now it is fine to insist that users declare their entry points using traditional syntax. This change strives to avoid unecessary changes after parsing, but if the new syntax catches on with users there are some small ways we can take advantage of it for performance. In particular, since `let` declarations and parameters of modern-style functions are immutable, we do not need to generate read/write local temporaries for them during lowering to the IR (technically we can make the same optimization for `const` locals). In the process of implementing these new forms I also added a few subroutines to help share code better between existing cases in the parser. In particular, parsing of generic parameter lists on declarations that can be generic is now simplified and more unified. * Fixup: remove leftover debugging code * fixup: typos
Diffstat (limited to 'source')
-rw-r--r--source/slang/check.cpp122
-rw-r--r--source/slang/decl-defs.h9
-rw-r--r--source/slang/diagnostic-defs.h3
-rw-r--r--source/slang/parser.cpp297
-rw-r--r--source/slang/syntax.h3
5 files changed, 299 insertions, 135 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 82936b65d..10714ca3a 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -2134,39 +2134,74 @@ namespace Slang
void CheckVarDeclCommon(RefPtr<VarDeclBase> varDecl)
{
- if (function || checkingPhase == CheckingPhase::Header)
+ // A variable that didn't have an explicit type written must
+ // have its type inferred from the initial-value expresison.
+ //
+ if(!varDecl->type.exp)
{
- TypeExp typeExp = CheckUsableType(varDecl->type);
- varDecl->type = typeExp;
- if (varDecl->type.Equals(getSession()->getVoidType()))
+ // In this case we need to perform all checking of the
+ // variable (including semantic checking of the initial-value
+ // expression) during the first phase of checking.
+
+ auto initExpr = varDecl->initExpr;
+ if(!initExpr)
{
- getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid);
+ getSink()->diagnose(varDecl, Diagnostics::varWithoutTypeMustHaveInitializer);
+ varDecl->type.type = getSession()->getErrorType();
}
- }
+ else
+ {
+ initExpr = CheckExpr(initExpr);
- if (checkingPhase == CheckingPhase::Body)
+ // TODO: We might need some additional steps here to ensure
+ // that the type of the expression is one we are okay with
+ // inferring. E.g., if we ever decide that integer and floating-point
+ // literals have a distinct type from the standard int/float types,
+ // then we would need to "decay" a literal to an explicit type here.
+
+ varDecl->initExpr = initExpr;
+ varDecl->type.type = initExpr->type;
+ }
+
+ varDecl->SetCheckState(DeclCheckState::Checked);
+ }
+ else
{
- if (auto initExpr = varDecl->initExpr)
+ if (function || checkingPhase == CheckingPhase::Header)
{
- initExpr = CheckTerm(initExpr);
- initExpr = Coerce(varDecl->type.Ptr(), initExpr);
- varDecl->initExpr = initExpr;
+ TypeExp typeExp = CheckUsableType(varDecl->type);
+ varDecl->type = typeExp;
+ if (varDecl->type.Equals(getSession()->getVoidType()))
+ {
+ getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid);
+ }
+ }
- // If this is an array variable, then we first want to give
- // it a chance to infer an array size from its initializer
- //
- // TODO(tfoley): May need to extend this to handle the
- // multi-dimensional case...
- //
- maybeInferArraySizeForVariable(varDecl);
- //
- // Next we want to make sure that the declared (or inferred)
- // size for the array meets whatever language-specific
- // constraints we want to enforce (e.g., disallow empty
- // arrays in specific cases)
- //
- validateArraySizeForVariable(varDecl);
+ if (checkingPhase == CheckingPhase::Body)
+ {
+ if (auto initExpr = varDecl->initExpr)
+ {
+ initExpr = CheckTerm(initExpr);
+ initExpr = Coerce(varDecl->type.Ptr(), initExpr);
+ varDecl->initExpr = initExpr;
+
+ // If this is an array variable, then we first want to give
+ // it a chance to infer an array size from its initializer
+ //
+ // TODO(tfoley): May need to extend this to handle the
+ // multi-dimensional case...
+ //
+ maybeInferArraySizeForVariable(varDecl);
+ //
+ // Next we want to make sure that the declared (or inferred)
+ // size for the array meets whatever language-specific
+ // constraints we want to enforce (e.g., disallow empty
+ // arrays in specific cases)
+ //
+ validateArraySizeForVariable(varDecl);
+ }
}
+
}
varDecl->SetCheckState(getCheckedState());
}
@@ -4289,8 +4324,19 @@ namespace Slang
functionNode->SetCheckState(DeclCheckState::CheckingHeader);
auto oldFunc = this->function;
this->function = functionNode;
- auto returnType = CheckProperType(functionNode->ReturnType);
- functionNode->ReturnType = returnType;
+
+ auto resultType = functionNode->ReturnType;
+ if(resultType.exp)
+ {
+ resultType = CheckProperType(functionNode->ReturnType);
+ }
+ else
+ {
+ resultType = TypeExp(getSession()->getVoidType());
+ }
+ functionNode->ReturnType = resultType;
+
+
HashSet<Name*> paraNames;
for (auto & para : functionNode->GetParameters())
{
@@ -9535,6 +9581,28 @@ namespace Slang
if(isGlobalShaderParameter(varDeclRef.getDecl()))
isLValue = false;
+ // Variables declared with `let` are always immutable.
+ if(varDeclRef.As<LetDecl>())
+ isLValue = false;
+
+ // Generic value parameters are always immutable
+ if(varDeclRef.As<GenericValueParamDecl>())
+ isLValue = false;
+
+ // Function parameters declared in the "modern" style
+ // are immutable unless they have an `out` or `inout` modifier.
+ if( varDeclRef.As<ModernParamDecl>() )
+ {
+ // Note: the `inout` modifier AST class inherits from
+ // the class for the `out` modifier so that we can
+ // make simple checks like this.
+ //
+ if( !varDeclRef.getDecl()->HasModifier<OutModifier>() )
+ {
+ isLValue = false;
+ }
+ }
+
qualType.IsLeftValue = isLValue;
return qualType;
}
diff --git a/source/slang/decl-defs.h b/source/slang/decl-defs.h
index 076db1188..c0d31365e 100644
--- a/source/slang/decl-defs.h
+++ b/source/slang/decl-defs.h
@@ -51,6 +51,10 @@ END_SYNTAX_CLASS()
SYNTAX_CLASS(VarDecl, VarDeclBase)
END_SYNTAX_CLASS()
+// A variable declaration that is always immutable (whether local, global, or member variable)
+SYNTAX_CLASS(LetDecl, VarDecl)
+END_SYNTAX_CLASS()
+
// An `AggTypeDeclBase` captures the shared functionality
// between true aggregate type declarations and extension
// declarations:
@@ -165,6 +169,8 @@ SYNTAX_CLASS(TypeDefDecl, SimpleTypeDecl)
SYNTAX_FIELD(TypeExp, type)
END_SYNTAX_CLASS()
+SIMPLE_SYNTAX_CLASS(TypeAliasDecl, TypeDefDecl)
+
// An 'assoctype' declaration, it is a container of inheritance clauses
SYNTAX_CLASS(AssocTypeDecl, AggTypeDecl)
END_SYNTAX_CLASS()
@@ -180,6 +186,9 @@ SIMPLE_SYNTAX_CLASS(ScopeDecl, ContainerDecl)
// A function/initializer/subscript parameter (potentially mutable)
SIMPLE_SYNTAX_CLASS(ParamDecl, VarDeclBase)
+// A parameter of a function declared in "modern" types (immutable unless explicitly `out` or `inout`)
+SIMPLE_SYNTAX_CLASS(ModernParamDecl, ParamDecl)
+
// Base class for things that have parameter lists and can thus be applied to arguments ("called")
ABSTRACT_SYNTAX_CLASS(CallableDecl, ContainerDecl)
RAW(
diff --git a/source/slang/diagnostic-defs.h b/source/slang/diagnostic-defs.h
index ceb4706fd..2c4f00dd6 100644
--- a/source/slang/diagnostic-defs.h
+++ b/source/slang/diagnostic-defs.h
@@ -289,7 +289,8 @@ DIAGNOSTIC(30501, Error, cannotUseInitializerListForArrayOfUnknownSize, "cannot
DIAGNOSTIC(30502, Error, cannotUseInitializerListForVectorOfUnknownSize, "cannot use initializer list for vector of statically unknown size '$0'");
DIAGNOSTIC(30503, Error, cannotUseInitializerListForMatrixOfUnknownSize, "cannot use initializer list for matrix of statically unknown size '$0' rows");
-
+// 306xx: variables
+DIAGNOSTIC(30600, Error, varWithoutTypeMustHaveInitializer, "a variable declaration without an initial-value expression must be given an explicit type");
DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')")
DIAGNOSTIC(39999, Error, expectedIntegerConstantNotConstant, "expression does not evaluate to a compile-time constant")
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index a21d57b05..5cc048cf5 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -1163,6 +1163,7 @@ namespace Slang
parser->ReadToken(TokenType::OpGreater);
decl->inner = parseInnerFunc(decl);
decl->inner->ParentDecl = decl;
+
// A generic decl hijacks the name of the declaration
// it wraps, so that lookup can find it.
if (decl->inner)
@@ -1172,6 +1173,26 @@ namespace Slang
}
}
+ template<typename ParseFunc>
+ static RefPtr<Decl> parseOptGenericDecl(
+ Parser* parser, const ParseFunc& parseInner)
+ {
+ // TODO: may want more advanced disambiguation than this...
+ if (parser->LookAheadToken(TokenType::OpLess))
+ {
+ RefPtr<GenericDecl> genericDecl = new GenericDecl();
+ parser->FillPosition(genericDecl);
+ parser->PushScope(genericDecl);
+ ParseGenericDeclImpl(parser, genericDecl, parseInner);
+ parser->PopScope();
+ return genericDecl;
+ }
+ else
+ {
+ return parseInner(nullptr);
+ }
+ }
+
static RefPtr<RefObject> ParseGenericDecl(Parser* parser, void*)
{
RefPtr<GenericDecl> decl = new GenericDecl();
@@ -1244,79 +1265,59 @@ namespace Slang
{}
};
- static RefPtr<Decl> ParseFuncDeclHeader(
- Parser* parser,
- DeclaratorInfo const& declaratorInfo,
- RefPtr<FuncDecl> decl,
- RefPtr<GenericDecl> genDecl)
+ /// Parse an optional body statement for a declaration that can have a body.
+ static RefPtr<Stmt> parseOptBody(Parser* parser)
{
- RefPtr<Decl> retDecl = decl;
-
- parser->FillPosition(decl.Ptr());
- decl->loc = declaratorInfo.nameAndLoc.loc;
-
- decl->nameAndLoc = declaratorInfo.nameAndLoc;
-
- // if return type is a DeclRef type, we need to update its scope to use this function decl's scope
- // so that LookUp can find the generic type parameters declared after the function name
- ReplaceScopeVisitor replaceScopeVisitor;
- replaceScopeVisitor.scope = parser->currentScope;
- declaratorInfo.typeSpec->accept(&replaceScopeVisitor, nullptr);
-
- decl->ReturnType = TypeExp(declaratorInfo.typeSpec);
- auto parseFuncDeclHeaderInner = [&](GenericDecl *)
- {
- parseParameterList(parser, decl);
- ParseOptSemantics(parser, decl.Ptr());
- return decl;
- };
-
- if (parser->LookAheadToken(TokenType::OpLess))
+ if (AdvanceIf(parser, TokenType::Semicolon))
{
- // parse generic parameters
- ParseGenericDeclImpl(parser, genDecl.Ptr(), parseFuncDeclHeaderInner);
- retDecl = genDecl;
+ // empty body
+ return nullptr;
}
else
- parseFuncDeclHeaderInner(nullptr);
-
- return retDecl;
+ {
+ return parser->parseBlockStatement();
+ }
}
- static RefPtr<Decl> ParseFuncDecl(
+ /// Complete parsing of a function using traditional (C-like) declarator syntax
+ static RefPtr<Decl> parseTraditionalFuncDecl(
Parser* parser,
- ContainerDecl* /*containerDecl*/,
- DeclaratorInfo const& declaratorInfo,
- bool isGeneric)
+ DeclaratorInfo const& declaratorInfo)
{
RefPtr<FuncDecl> decl = new FuncDecl();
- RefPtr<Decl> retDecl = decl;
- RefPtr<GenericDecl> genDecl;
- if (isGeneric)
- {
- genDecl = new GenericDecl();
- parser->FillPosition(genDecl);
- parser->PushScope(genDecl);
- retDecl = genDecl;
- }
- parser->PushScope(decl.Ptr());
- ParseFuncDeclHeader(parser, declaratorInfo, decl, genDecl);
+ parser->FillPosition(decl.Ptr());
+ decl->loc = declaratorInfo.nameAndLoc.loc;
+ decl->nameAndLoc = declaratorInfo.nameAndLoc;
- if (AdvanceIf(parser, TokenType::Semicolon))
+ return parseOptGenericDecl(parser, [&](GenericDecl*)
{
- // empty body
- }
- else
- {
- decl->Body = parser->parseBlockStatement();
- }
+ // HACK: The return type of the function will already have been
+ // parsed in a scope that didn't include the function's generic
+ // parameters.
+ //
+ // We will use a visitor here to try and replace the scope associated
+ // with any name expressiosn in the reuslt type.
+ //
+ // TODO: This should be fixed by not associating scopes with
+ // such expressions at parse time, and instead pushing down scopes
+ // as part of the state during semantic checking.
+ //
+ ReplaceScopeVisitor replaceScopeVisitor;
+ replaceScopeVisitor.scope = parser->currentScope;
+ declaratorInfo.typeSpec->accept(&replaceScopeVisitor, nullptr);
+
+ decl->ReturnType = TypeExp(declaratorInfo.typeSpec);
+
+ parser->PushScope(decl);
+
+ parseParameterList(parser, decl);
+ ParseOptSemantics(parser, decl.Ptr());
+ decl->Body = parseOptBody(parser);
- parser->PopScope();
- if (isGeneric)
- {
parser->PopScope();
- }
- return retDecl;
+
+ return decl;
+ });
}
static RefPtr<VarDeclBase> CreateVarDeclForContext(
@@ -1971,7 +1972,7 @@ namespace Slang
{
// Looks like a function, so parse it like one.
UnwrapDeclarator(initDeclarator, &declaratorInfo);
- return ParseFuncDecl(parser, containerDecl, declaratorInfo, parser->tokenReader.PeekTokenType() == TokenType::OpLess);
+ return parseTraditionalFuncDecl(parser, declaratorInfo);
}
// Otherwise we are looking at a variable declaration, which could be one in a sequence...
@@ -2593,14 +2594,8 @@ namespace Slang
parseParameterList(parser, decl);
- if( AdvanceIf(parser, TokenType::Semicolon) )
- {
- // empty body
- }
- else
- {
- decl->Body = parser->parseBlockStatement();
- }
+ decl->Body = parseOptBody(parser);
+
return decl;
}
@@ -2675,9 +2670,124 @@ namespace Slang
return decl;
}
- static Token expect(Parser* parser, TokenType tokenType)
+ static bool expect(Parser* parser, TokenType tokenType)
{
- return parser->ReadToken(tokenType);
+ return parser->ReadToken(tokenType).type == tokenType;
+ }
+
+ static void parseModernVarDeclBaseCommon(
+ Parser* parser,
+ RefPtr<VarDeclBase> decl)
+ {
+ parser->FillPosition(decl.Ptr());
+ decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
+
+ if(AdvanceIf(parser, TokenType::Colon))
+ {
+ decl->type = parser->ParseTypeExp();
+ }
+
+ if(AdvanceIf(parser, TokenType::OpAssign))
+ {
+ decl->initExpr = parser->ParseInitExpr();
+ }
+ }
+
+ static void parseModernVarDeclCommon(
+ Parser* parser,
+ RefPtr<VarDecl> decl)
+ {
+ parseModernVarDeclBaseCommon(parser, decl);
+ expect(parser, TokenType::Semicolon);
+ }
+
+ static RefPtr<RefObject> parseLetDecl(
+ Parser* parser, void* /*userData*/)
+ {
+ RefPtr<LetDecl> decl = new LetDecl();
+ parseModernVarDeclCommon(parser, decl);
+ return decl;
+ }
+
+ static RefPtr<RefObject> parseVarDecl(
+ Parser* parser, void* /*userData*/)
+ {
+ RefPtr<VarDecl> decl = new VarDecl();
+ parseModernVarDeclCommon(parser, decl);
+ return decl;
+ }
+
+ static RefPtr<ParamDecl> parseModernParamDecl(
+ Parser* parser)
+ {
+ RefPtr<ParamDecl> decl = new ParamDecl();
+
+ // TODO: "modern" parameters should not accept keyword-based
+ // modifiers and should only accept `[attribute]` syntax for
+ // modifiers to keep the grammar as simple as possible.
+ //
+ // Further, they should accept `out` and `in out`/`inout`
+ // before the type (e.g., `a: inout float4`).
+ //
+ decl->modifiers = ParseModifiers(parser);
+ parseModernVarDeclBaseCommon(parser, decl);
+ return decl;
+ }
+
+ static void parseModernParamList(
+ Parser* parser,
+ RefPtr<CallableDecl> decl)
+ {
+ parser->ReadToken(TokenType::LParent);
+
+ while (!AdvanceIfMatch(parser, TokenType::RParent))
+ {
+ AddMember(decl, parseModernParamDecl(parser));
+ if (AdvanceIf(parser, TokenType::RParent))
+ break;
+ parser->ReadToken(TokenType::Comma);
+ }
+ }
+
+ static RefPtr<RefObject> parseFuncDecl(
+ Parser* parser, void* /*userData*/)
+ {
+ RefPtr<FuncDecl> decl = new FuncDecl();
+
+ parser->FillPosition(decl.Ptr());
+ decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
+
+ return parseOptGenericDecl(parser, [&](GenericDecl*)
+ {
+ parser->PushScope(decl.Ptr());
+ parseModernParamList(parser, decl);
+ if(AdvanceIf(parser, TokenType::RightArrow))
+ {
+ decl->ReturnType = parser->ParseTypeExp();
+ }
+ decl->Body = parseOptBody(parser);
+ parser->PopScope();
+ return decl;
+ });
+ }
+
+ static RefPtr<RefObject> parseTypeAliasDecl(
+ Parser* parser, void* /*userData*/)
+ {
+ RefPtr<TypeAliasDecl> decl = new TypeAliasDecl();
+
+ parser->FillPosition(decl.Ptr());
+ decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier));
+
+ return parseOptGenericDecl(parser, [&](GenericDecl*)
+ {
+ if( expect(parser, TokenType::OpAssign) )
+ {
+ decl->type = parser->ParseTypeExp();
+ }
+ expect(parser, TokenType::Semicolon);
+ return decl;
+ });
}
// This is a catch-all syntax-construction callback to handle cases where
@@ -3084,36 +3194,20 @@ namespace Slang
RefPtr<Decl> Parser::ParseStruct()
{
RefPtr<StructDecl> rs = new StructDecl();
- RefPtr<Decl> retDecl = rs;
FillPosition(rs.Ptr());
ReadToken("struct");
// TODO: support `struct` declaration without tag
rs->nameAndLoc = expectIdentifier(this);
- auto parseStructInner = [&](GenericDecl*)
+ return parseOptGenericDecl(this, [&](GenericDecl*)
{
// We allow for an inheritance clause on a `struct`
// so that it can conform to interfaces.
parseOptionalInheritanceClause(this, rs.Ptr());
parseAggTypeDeclBody(this, rs.Ptr());
return rs;
- };
-
- if (LookAheadToken(TokenType::OpLess))
- {
- RefPtr<GenericDecl> genDecl = new GenericDecl();
- FillPosition(genDecl.Ptr());
- PushScope(genDecl);
- ParseGenericDeclImpl(this, genDecl.Ptr(), parseStructInner);
- PopScope();
- retDecl = genDecl;
- }
- else
- {
- parseStructInner(nullptr);
- }
- return retDecl;
+ });
}
RefPtr<ClassDecl> Parser::ParseClass()
@@ -3158,7 +3252,8 @@ namespace Slang
decl->nameAndLoc = expectIdentifier(parser);
- auto parseEnumDeclInner = [&](GenericDecl*)
+
+ return parseOptGenericDecl(parser, [&](GenericDecl*)
{
parseOptionalInheritanceClause(parser, decl);
parser->ReadToken(TokenType::LBrace);
@@ -3174,23 +3269,7 @@ namespace Slang
parser->ReadToken(TokenType::Comma);
}
return decl;
- };
-
- if (parser->LookAheadToken(TokenType::OpLess))
- {
- RefPtr<GenericDecl> genericDecl = new GenericDecl();
- parser->FillPosition(genericDecl);
- parser->PushScope(genericDecl);
- ParseGenericDeclImpl(parser, genericDecl, parseEnumDeclInner);
- parser->PopScope();
- return genericDecl;
- }
- else
- {
- parseEnumDeclInner(nullptr);
- }
-
- return decl;
+ });
}
static RefPtr<Stmt> ParseSwitchStmt(Parser* parser)
@@ -4717,6 +4796,10 @@ namespace Slang
DECL(attribute_syntax,parseAttributeSyntaxDecl);
DECL(__import, parseImportDecl);
DECL(import, parseImportDecl);
+ DECL(let, parseLetDecl);
+ DECL(var, parseVarDecl);
+ DECL(func, parseFuncDecl);
+ DECL(typealias, parseTypeAliasDecl);
#undef DECL
diff --git a/source/slang/syntax.h b/source/slang/syntax.h
index 9b0277b56..74eda66a5 100644
--- a/source/slang/syntax.h
+++ b/source/slang/syntax.h
@@ -752,6 +752,9 @@ namespace Slang
explicit TypeExp(RefPtr<Expr> exp)
: exp(exp)
{}
+ explicit TypeExp(RefPtr<Type> type)
+ : type(type)
+ {}
TypeExp(RefPtr<Expr> exp, RefPtr<Type> type)
: exp(exp)
, type(type)