summaryrefslogtreecommitdiffstats
path: root/source/slang/check.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/check.cpp')
-rw-r--r--source/slang/check.cpp629
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)