diff options
Diffstat (limited to 'source/slang/check.cpp')
| -rw-r--r-- | source/slang/check.cpp | 629 |
1 files changed, 425 insertions, 204 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 04c41c4b0..2fa4e9bc7 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -1319,48 +1319,228 @@ namespace Slang return kConversionCost_Explicit; } - // Central engine for implementing implicit coercion logic - bool TryCoerceImpl( - RefPtr<Type> toType, // the target type for conversion - RefPtr<Expr>* outToExpr, // (optional) a place to stuff the target expression - RefPtr<Type> fromType, // the source type for the conversion - RefPtr<Expr> fromExpr, // the source expression - ConversionCost* outCost) // (optional) a place to stuff the conversion cost + bool isEffectivelyScalarForInitializerLists( + RefPtr<Type> type) { - // Easy case: the types are equal - if (toType->Equals(fromType)) + if(type->As<ArrayExpressionType>()) return false; + if(type->As<VectorExpressionType>()) return false; + if(type->As<MatrixExpressionType>()) return false; + + if(type->As<BasicExpressionType>()) { - if (outToExpr) - *outToExpr = fromExpr; - if (outCost) - *outCost = kConversionCost_None; return true; } - // If either type is an error, then let things pass. - if (toType->As<ErrorType>() || fromType->As<ErrorType>()) + if(type->As<ResourceType>()) + { + return true; + } + if(type->As<UntypedBufferResourceType>()) + { + return true; + } + if(type->As<SamplerStateType>()) { - if (outToExpr) - *outToExpr = CreateImplicitCastExpr(toType, fromExpr); - if (outCost) - *outCost = kConversionCost_None; return true; } - // Coercion from an initializer list is allowed for many types - if( auto fromInitializerListExpr = fromExpr.As<InitializerListExpr>()) + if(auto declRefType = type->As<DeclRefType>()) { - auto argCount = fromInitializerListExpr->args.Count(); + if(declRefType->declRef.As<StructDecl>()) + return false; + } - // In the case where we need to build a reuslt expression, - // we will collect the new arguments here - List<RefPtr<Expr>> coercedArgs; + return true; + } + + /// Should the provided expression (from an initializer list) be used directly to initialize `toType`? + bool shouldUseInitializerDirectly( + RefPtr<Type> toType, + RefPtr<Expr> fromExpr) + { + // A nested initializer list should always be used directly. + // + if(fromExpr.As<InitializerListExpr>()) + { + return true; + } + + // If the desired type is a scalar, then we should always initialize + // directly, since it isn't an aggregate. + // + if(isEffectivelyScalarForInitializerLists(toType)) + return true; + + // If the type we are initializing isn't effectively scalar, + // but the initialization expression *is*, then it doesn't + // seem like direct initialization is intended. + // + if(isEffectivelyScalarForInitializerLists(fromExpr->type)) + return false; + + // Once the above cases are handled, the main thing + // we want to check for is whether a direct initialization + // is possible (a type conversion exists). + // + return CanCoerce(toType, fromExpr->type); + } + + bool tryReadArgFromInitializerList( + RefPtr<Type> toType, + RefPtr<Expr>* outToExpr, + RefPtr<InitializerListExpr> fromInitializerListExpr, + UInt &ioInitArgIndex) + { + // First, we will check if we have run out of arguments + // on the initializer list. + // + UInt initArgCount = fromInitializerListExpr->args.Count(); + if(ioInitArgIndex >= initArgCount) + { + // If we are at the end of the initializer list, + // then our ability to read an argument depends + // on whether the type we are trying to read + // is default-initializable. + // + // For now, we will just pretend like everything + // is default-initializable and move along. + return true; + } + + // Okay, we have at least one initializer list expression, + // so we will look at the next expression and decide + // whether to use it to initialize the desired type + // directly (possibly via casts), or as the first sub-expression + // for aggregate initialization. + // + auto firstInitExpr = fromInitializerListExpr->args[ioInitArgIndex]; + if(shouldUseInitializerDirectly(toType, firstInitExpr)) + { + ioInitArgIndex++; + return TryCoerceImpl( + toType, + outToExpr, + firstInitExpr->type, + firstInitExpr, + nullptr); + } + + // If there is somehow an error in one of the initialization + // expressions, then everything could be thrown off and we + // shouldn't keep trying to read arguments. + // + if( IsErrorExpr(firstInitExpr) ) + { + // Stop reading arguments, as if we'd reached + // the end of the list. + // + ioInitArgIndex = initArgCount; + return true; + } + + // The fallback case is to recursively read the + // type from the same list as an aggregate. + // + return tryReadAggregateFromInitializerList( + toType, + outToExpr, + fromInitializerListExpr, + ioInitArgIndex); + } + + bool tryReadAggregateFromInitializerList( + RefPtr<Type> inToType, + RefPtr<Expr>* outToExpr, + RefPtr<InitializerListExpr> fromInitializerListExpr, + UInt &ioArgIndex) + { + auto toType = inToType; + UInt argCount = fromInitializerListExpr->args.Count(); - if (auto toVecType = toType->As<VectorExpressionType>()) + // In the case where we need to build a reuslt expression, + // we will collect the new arguments here + List<RefPtr<Expr>> coercedArgs; + + if(isEffectivelyScalarForInitializerLists(toType)) + { + // For any type that is effectively a non-aggregate, + // we expect to read a single value from the initializer list + // + if(ioArgIndex < argCount) + { + auto arg = fromInitializerListExpr->args[ioArgIndex++]; + return TryCoerceImpl( + toType, + outToExpr, + arg->type, + arg, + nullptr); + } + else + { + // If there wasn't an initialization + // expression to be found, then we need + // to perform default initialization here. + // + // We will let this case come through the front-end + // as an `InitializerListExpr` with zero arguments, + // and then have the IR generation logic deal with + // synthesizing default values. + } + } + else if (auto toVecType = toType->As<VectorExpressionType>()) + { + auto toElementCount = toVecType->elementCount; + auto toElementType = toVecType->elementType; + + UInt elementCount = 0; + if (auto constElementCount = toElementCount.As<ConstantIntVal>()) + { + elementCount = (UInt) constElementCount->value; + } + else + { + // We don't know the element count statically, + // so what are we supposed to be doing? + // + if(outToExpr) + { + getSink()->diagnose(fromInitializerListExpr, Diagnostics::cannotUseInitializerListForVectorOfUnknownSize, toElementCount); + } + return false; + } + + for(UInt ee = 0; ee < elementCount; ++ee) { - auto toElementCount = toVecType->elementCount; - auto toElementType = toVecType->elementType; + RefPtr<Expr> coercedArg; + bool argResult = tryReadArgFromInitializerList( + toElementType, + outToExpr ? &coercedArg : nullptr, + fromInitializerListExpr, + ioArgIndex); + + // No point in trying further if any argument fails + if(!argResult) + return false; + + if( coercedArg ) + { + coercedArgs.Add(coercedArg); + } + } + } + else if(auto toArrayType = toType->As<ArrayExpressionType>()) + { + // TODO(tfoley): If we can compute the size of the array statically, + // then we want to check that there aren't too many initializers present + + auto toElementType = toArrayType->baseType; + if(auto toElementCount = toArrayType->ArrayLength) + { + // In the case of a sized array, we need to check that the number + // of elements being initialized matches what was declared. + // UInt elementCount = 0; if (auto constElementCount = toElementCount.As<ConstantIntVal>()) { @@ -1370,127 +1550,234 @@ namespace Slang { // We don't know the element count statically, // so what are we supposed to be doing? - elementCount = fromInitializerListExpr->args.Count(); + // + if(outToExpr) + { + getSink()->diagnose(fromInitializerListExpr, Diagnostics::cannotUseInitializerListForArrayOfUnknownSize, toElementCount); + } + return false; } - // TODO: need to check that the element count - // for the vector type matches the argument - // count for the initializer list, or else - // fix them up to match. - - for(auto arg : fromInitializerListExpr->args) + for(UInt ee = 0; ee < elementCount; ++ee) { RefPtr<Expr> coercedArg; - ConversionCost argCost; - - bool argResult = TryCoerceImpl( + bool argResult = tryReadArgFromInitializerList( toElementType, outToExpr ? &coercedArg : nullptr, - arg->type, - arg, - outCost ? &argCost : nullptr); + fromInitializerListExpr, + ioArgIndex); // No point in trying further if any argument fails if(!argResult) return false; - // TODO(tfoley): what to do with cost? - // This only matters if/when we allow an initializer list as an argument to - // an overloaded call. - - if( outToExpr ) + if( coercedArg ) { coercedArgs.Add(coercedArg); } } } - // - // TODO(tfoley): How to handle matrices here? - // Should they expect individual scalars, or support - // vectors for the rows? - // - if(auto toDeclRefType = toType->As<DeclRefType>()) + else { - auto toTypeDeclRef = toDeclRefType->declRef; - if(auto toStructDeclRef = toTypeDeclRef.As<StructDecl>()) + // In the case of an unsized array type, we will use the + // number of arguments to the initializer to determine + // the element count. + // + UInt elementCount = 0; + while(ioArgIndex < argCount) { - // Trying to initialize a `struct` type given an initializer list. - // We will go through the fields in order and try to match them - // up with initializer arguments. - + RefPtr<Expr> coercedArg; + bool argResult = tryReadArgFromInitializerList( + toElementType, + outToExpr ? &coercedArg : nullptr, + fromInitializerListExpr, + ioArgIndex); - UInt argIndex = 0; - for(auto fieldDeclRef : getMembersOfType<StructField>(toStructDeclRef)) - { - if(argIndex >= argCount) - { - // We've consumed all the arguments, so we should stop - break; - } + // No point in trying further if any argument fails + if(!argResult) + return false; - auto arg = fromInitializerListExpr->args[argIndex++]; + elementCount++; - // - RefPtr<Expr> coercedArg; - ConversionCost argCost; + if( coercedArg ) + { + coercedArgs.Add(coercedArg); + } + } - bool argResult = TryCoerceImpl( - GetType(fieldDeclRef), - outToExpr ? &coercedArg : nullptr, - arg->type, - arg, - outCost ? &argCost : nullptr); + // We have a new type for the conversion, based on what + // we learned. + toType = getSession()->getArrayType( + toElementType, + new ConstantIntVal(elementCount)); + } + } + else if(auto toMatrixType = toType->As<MatrixExpressionType>()) + { + // In the general case, the initializer list might comprise + // both vectors and scalars. + // + // The traditional HLSL compilers treat any vectors in + // the initializer list exactly equivalent to their sequence + // of scalar elements, and don't care how this might, or + // might not, align with the rows of the matrix. + // + // We will draw a line in the sand and say that an initializer + // list for a matrix will act as if the matrix type were an + // array of vectors for the rows. - // No point in trying further if any argument fails - if(!argResult) - return false; - // TODO(tfoley): what to do with cost? - // This only matters if/when we allow an initializer list as an argument to - // an overloaded call. + UInt rowCount = 0; + auto toRowType = createVectorType( + toMatrixType->getElementType(), + toMatrixType->getColumnCount()); - if( outToExpr ) - { - coercedArgs.Add(coercedArg); - } - } + if (auto constRowCount = dynamic_cast<ConstantIntVal*>(toMatrixType->getRowCount())) + { + rowCount = (UInt) constRowCount->value; + } + else + { + // We don't know the element count statically, + // so what are we supposed to be doing? + // + if(outToExpr) + { + getSink()->diagnose(fromInitializerListExpr, Diagnostics::cannotUseInitializerListForMatrixOfUnknownSize, toMatrixType->getRowCount()); } + return false; } - else if(auto toArrayType = toType->As<ArrayExpressionType>()) + + for(UInt rr = 0; rr < rowCount; ++rr) { - // TODO(tfoley): If we can compute the size of the array statically, - // then we want to check that there aren't too many initializers present + RefPtr<Expr> coercedArg; + bool argResult = tryReadArgFromInitializerList( + toRowType, + outToExpr ? &coercedArg : nullptr, + fromInitializerListExpr, + ioArgIndex); - auto toElementType = toArrayType->baseType; + // No point in trying further if any argument fails + if(!argResult) + return false; - for(auto& arg : fromInitializerListExpr->args) + if( coercedArg ) + { + coercedArgs.Add(coercedArg); + } + } + } + else if(auto toDeclRefType = toType->As<DeclRefType>()) + { + auto toTypeDeclRef = toDeclRefType->declRef; + if(auto toStructDeclRef = toTypeDeclRef.As<StructDecl>()) + { + // Trying to initialize a `struct` type given an initializer list. + // We will go through the fields in order and try to match them + // up with initializer arguments. + // + for(auto fieldDeclRef : getMembersOfType<StructField>(toStructDeclRef)) { RefPtr<Expr> coercedArg; - ConversionCost argCost; - - bool argResult = TryCoerceImpl( - toElementType, - outToExpr ? &coercedArg : nullptr, - arg->type, - arg, - outCost ? &argCost : nullptr); + bool argResult = tryReadArgFromInitializerList( + GetType(fieldDeclRef), + outToExpr ? &coercedArg : nullptr, + fromInitializerListExpr, + ioArgIndex); // No point in trying further if any argument fails if(!argResult) return false; - if( outToExpr ) + if( coercedArg ) { coercedArgs.Add(coercedArg); } } } - else - { - // By default, we don't allow a type to be initialized using - // an initializer list. + } + else + { + // We shouldn't get to this case in practice, + // but just in case we'll consider an initializer + // list invalid if we are trying to read something + // off of it that wasn't handled by the cases above. + // + return false; + } + + // We were able to coerce all the arguments given, and so + // we need to construct a suitable expression to remember the result + // + if(outToExpr) + { + auto toInitializerListExpr = new InitializerListExpr(); + toInitializerListExpr->loc = fromInitializerListExpr->loc; + toInitializerListExpr->type = QualType(toType); + toInitializerListExpr->args = coercedArgs; + + *outToExpr = toInitializerListExpr; + } + + return true; + } + + bool tryCoerceInitializerList( + RefPtr<Type> toType, + RefPtr<Expr>* outToExpr, + RefPtr<InitializerListExpr> fromInitializerListExpr) + { + UInt argCount = fromInitializerListExpr->args.Count(); + UInt argIndex = 0; + + // TODO: we should handle the special case of `{0}` as an initializer + // for arbitrary `struct` types here. + + if(!tryReadAggregateFromInitializerList(toType, outToExpr, fromInitializerListExpr, argIndex)) + return false; + + if(argIndex != argCount) + { + getSink()->diagnose(fromInitializerListExpr, Diagnostics::tooManyInitializers, argIndex, argCount); + } + + return true; + } + + + // Central engine for implementing implicit coercion logic + bool TryCoerceImpl( + RefPtr<Type> toType, // the target type for conversion + RefPtr<Expr>* outToExpr, // (optional) a place to stuff the target expression + RefPtr<Type> fromType, // the source type for the conversion + RefPtr<Expr> fromExpr, // the source expression + ConversionCost* outCost) // (optional) a place to stuff the conversion cost + { + // Easy case: the types are equal + if (toType->Equals(fromType)) + { + if (outToExpr) + *outToExpr = fromExpr; + if (outCost) + *outCost = kConversionCost_None; + return true; + } + + // If either type is an error, then let things pass. + if (toType->As<ErrorType>() || fromType->As<ErrorType>()) + { + if (outToExpr) + *outToExpr = CreateImplicitCastExpr(toType, fromExpr); + if (outCost) + *outCost = kConversionCost_None; + return true; + } + + // Coercion from an initializer list is allowed for many types + if( auto fromInitializerListExpr = fromExpr.As<InitializerListExpr>()) + { + if(!tryCoerceInitializerList(toType, outToExpr, fromInitializerListExpr)) return false; - } // For now, coercion from an initializer list has no cost if(outCost) @@ -1498,19 +1785,6 @@ namespace Slang *outCost = kConversionCost_None; } - // We were able to coerce all the arguments given, and so - // we need to construct a suitable expression to remember the result - if(outToExpr) - { - auto toInitializerListExpr = new InitializerListExpr(); - toInitializerListExpr->loc = fromInitializerListExpr->loc; - toInitializerListExpr->type = QualType(toType); - toInitializerListExpr->args = coercedArgs; - - - *outToExpr = toInitializerListExpr; - } - return true; } @@ -1859,42 +2133,41 @@ namespace Slang void CheckVarDeclCommon(RefPtr<VarDeclBase> varDecl) { - // Check the type, if one was given - TypeExp type = CheckUsableType(varDecl->type); - - // TODO: Additional validation rules on types should go here, - // but we need to deal with the fact that some cases might be - // allowed in one context (e.g., an unsized array parameter) - // but not in othters (e.g., an unsized array field in a struct). - - // Check the initializers, if one was given - RefPtr<Expr> initExpr = CheckTerm(varDecl->initExpr); - - // If a type was given, ... - if (type.Ptr()) + if (function || checkingPhase == CheckingPhase::Header) { - // then coerce any initializer to the type - if (initExpr) + TypeExp typeExp = CheckUsableType(varDecl->type); + varDecl->type = typeExp; + if (varDecl->type.Equals(getSession()->getVoidType())) { - initExpr = Coerce(type.Ptr(), initExpr); + getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid); } } - else - { - // TODO: infer a type from the initializers - if (!initExpr) - { - getSink()->diagnose(varDecl, Diagnostics::unimplemented, "variable declaration with no type must have initializer"); - } - else + if (checkingPhase == CheckingPhase::Body) + { + if (auto initExpr = varDecl->initExpr) { - getSink()->diagnose(varDecl, Diagnostics::unimplemented, "type inference for variable declaration"); + 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->type = type; - varDecl->initExpr = initExpr; + varDecl->SetCheckState(getCheckedState()); } // Fill in default substitutions for the 'subtype' part of a type constraint decl @@ -2535,12 +2808,7 @@ namespace Slang void visitStructField(StructField* field) { - if (field->IsChecked(DeclCheckState::CheckedHeader)) - return; - // TODO: bottleneck through general-case variable checking - - field->type = CheckUsableType(field->type); - field->SetCheckState(getCheckedState()); + CheckVarDeclCommon(field); } bool doesSignatureMatchRequirement( @@ -4295,7 +4563,7 @@ namespace Slang return 1; } - void maybeInferArraySizeForVariable(Variable* varDecl) + void maybeInferArraySizeForVariable(VarDeclBase* varDecl) { // Not an array? auto arrayType = varDecl->type->AsArrayType(); @@ -4309,14 +4577,8 @@ namespace Slang auto initExpr = varDecl->initExpr; if(!initExpr) return; - // Is the initializer an initializer list? - if(auto initializerListExpr = initExpr.As<InitializerListExpr>()) - { - auto argCount = initializerListExpr->args.Count(); - elementCount = new ConstantIntVal(argCount); - } // Is the type of the initializer an array type? - else if(auto arrayInitType = initExpr->type->As<ArrayExpressionType>()) + if(auto arrayInitType = initExpr->type->As<ArrayExpressionType>()) { elementCount = arrayInitType->ArrayLength; } @@ -4333,7 +4595,7 @@ namespace Slang elementCount); } - void ValidateArraySizeForVariable(Variable* varDecl) + void validateArraySizeForVariable(VarDeclBase* varDecl) { auto arrayType = varDecl->type->AsArrayType(); if (!arrayType) return; @@ -4360,48 +4622,7 @@ namespace Slang void visitVariable(Variable* varDecl) { - if (function || checkingPhase == CheckingPhase::Header) - { - TypeExp typeExp = CheckUsableType(varDecl->type); - varDecl->type = typeExp; - if (varDecl->type.Equals(getSession()->getVoidType())) - { - getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid); - } - } - - if (checkingPhase == CheckingPhase::Body) - { - if (auto initExpr = varDecl->initExpr) - { - initExpr = CheckTerm(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); - - - if (auto initExpr = varDecl->initExpr) - { - // TODO(tfoley): should coercion of initializer lists be special-cased - // here, or handled as a general case for coercion? - - initExpr = Coerce(varDecl->type.Ptr(), initExpr); - varDecl->initExpr = initExpr; - } - } - varDecl->SetCheckState(getCheckedState()); + CheckVarDeclCommon(varDecl); } void visitWhileStmt(WhileStmt *stmt) |
