diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/check.cpp | 122 | ||||
| -rw-r--r-- | source/slang/decl-defs.h | 9 | ||||
| -rw-r--r-- | source/slang/diagnostic-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 297 | ||||
| -rw-r--r-- | source/slang/syntax.h | 3 |
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) |
