summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
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)