summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-decl.cpp54
-rw-r--r--source/slang/slang-check-expr.cpp103
-rw-r--r--source/slang/slang-check-impl.h64
-rw-r--r--source/slang/slang-diagnostic-defs.h1
4 files changed, 170 insertions, 52 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index b69c8cf0d..3aeca23cd 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -775,6 +775,24 @@ namespace Slang
return true;
}
+ void SemanticsVisitor::_validateCircularVarDefinition(VarDeclBase* varDecl)
+ {
+ // The easiest way to test if the declaration is circular is to
+ // validate it as a constant.
+ //
+ // TODO: The logic here will only apply for `static const` declarations
+ // of integer type, given that our constant folding currently only
+ // applies to such types. A more robust fix would involve a truly
+ // recursive walk of the AST declarations, and an even *more* robust
+ // fix would wait until after IR linking to detect and diagnose circularity
+ // in case it crosses module boundaries.
+ //
+ //
+ if(!isScalarIntegerType(varDecl->type))
+ return;
+ tryConstantFoldDeclRef(DeclRef<VarDeclBase>(varDecl, nullptr), nullptr);
+ }
+
void SemanticsDeclHeaderVisitor::checkVarDeclCommon(VarDeclBase* varDecl)
{
// A variable that didn't have an explicit type written must
@@ -804,6 +822,8 @@ namespace Slang
varDecl->initExpr = initExpr;
varDecl->type.type = initExpr->type;
+
+ _validateCircularVarDefinition(varDecl);
}
// If we've gone down this path, then the variable
@@ -857,11 +877,16 @@ namespace Slang
{
// If the variable has an explicit initial-value expression,
// then we simply need to check that expression and coerce
- // it to the tyep of the variable.
+ // it to the type of the variable.
//
initExpr = CheckTerm(initExpr);
initExpr = coerce(varDecl->type.Ptr(), initExpr);
varDecl->initExpr = initExpr;
+
+ // We need to ensure that any variable doesn't introduce
+ // a constant with a circular definition.
+ //
+ _validateCircularVarDefinition(varDecl);
}
else
{
@@ -1970,18 +1995,25 @@ namespace Slang
return (BaseTypeInfo::getInfo(baseType).flags & BaseTypeInfo::Flag::Integer) != 0;
}
- void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc)
+ bool SemanticsVisitor::isScalarIntegerType(Type* type)
{
- if(auto basicType = as<BasicExpressionType>(type))
- {
- // Allow the built-in integer types.
- if(isIntegerBaseType(basicType->baseType))
- return;
+ auto basicType = as<BasicExpressionType>(type);
+ if(!basicType)
+ return false;
- // By default, don't allow other types to be used
- // as an `enum` tag type.
- }
+ return isIntegerBaseType(basicType->baseType);
+ }
+ void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc)
+ {
+ // Allow the built-in integer types.
+ //
+ if(isScalarIntegerType(type))
+ return;
+
+ // By default, don't allow other types to be used
+ // as an `enum` tag type.
+ //
getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type);
}
@@ -2177,7 +2209,7 @@ namespace Slang
// the tag value for a successor case that doesn't
// provide an explicit tag.
- IntVal* explicitTagVal = TryConstantFoldExpr(explicitTagValExpr);
+ IntVal* explicitTagVal = tryConstantFoldExpr(explicitTagValExpr, nullptr);
if(explicitTagVal)
{
if(auto constIntVal = as<ConstantIntVal>(explicitTagVal))
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index 17bd5e263..d2e5afd85 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -669,8 +669,9 @@ namespace Slang
return m_astBuilder->create<ConstantIntVal>(expr->value);
}
- IntVal* SemanticsVisitor::TryConstantFoldExpr(
- InvokeExpr* invokeExpr)
+ IntVal* SemanticsVisitor::tryConstantFoldExpr(
+ InvokeExpr* invokeExpr,
+ ConstantFoldingCircularityInfo* circularityInfo)
{
// We need all the operands to the expression
@@ -707,7 +708,7 @@ namespace Slang
bool allConst = true;
for (auto argExpr : invokeExpr->arguments)
{
- auto argVal = TryCheckIntegerConstantExpression(argExpr);
+ auto argVal = tryFoldIntegerConstantExpression(argExpr, circularityInfo);
if (!argVal)
return nullptr;
@@ -795,8 +796,53 @@ namespace Slang
return result;
}
- IntVal* SemanticsVisitor::TryConstantFoldExpr(
- Expr* expr)
+ bool SemanticsVisitor::_checkForCircularityInConstantFolding(
+ Decl* decl,
+ ConstantFoldingCircularityInfo* circularityInfo)
+ {
+ // TODO: If the `decl` is already on the chain of `circularityInfo`,
+ // then we know that we are trying to recursively fold the
+ // same declaration as part of its own definition, and we need
+ // to diagnose that as an error.
+ //
+ for( auto info = circularityInfo; info; info = info->next )
+ {
+ if(decl == info->decl)
+ {
+ getSink()->diagnose(decl, Diagnostics::variableUsedInItsOwnDefinition, decl);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ IntVal* SemanticsVisitor::tryConstantFoldDeclRef(
+ DeclRef<VarDeclBase> const& declRef,
+ ConstantFoldingCircularityInfo* circularityInfo)
+ {
+ auto decl = declRef.getDecl();
+
+ if(_checkForCircularityInConstantFolding(decl, circularityInfo))
+ return nullptr;
+
+ // In HLSL, `static const` is used to mark compile-time constant expressions
+ if(!decl->hasModifier<HLSLStaticModifier>())
+ return nullptr;
+ if(!decl->hasModifier<ConstModifier>())
+ return nullptr;
+
+ auto initExpr = getInitExpr(m_astBuilder, declRef);
+ if(!initExpr)
+ return nullptr;
+
+ ConstantFoldingCircularityInfo newCircularityInfo(decl, circularityInfo);
+ return tryConstantFoldExpr(initExpr, &newCircularityInfo);
+ }
+
+ IntVal* SemanticsVisitor::tryConstantFoldExpr(
+ Expr* expr,
+ ConstantFoldingCircularityInfo* circularityInfo)
{
// Unwrap any "identity" expressions
while (auto parenExpr = as<ParenExpr>(expr))
@@ -825,40 +871,32 @@ namespace Slang
// are defined in a way that can be used as a constant expression:
if(auto varRef = declRef.as<VarDeclBase>())
{
- auto varDecl = varRef.getDecl();
-
- // In HLSL, `static const` is used to mark compile-time constant expressions
- if(auto staticAttr = varDecl->findModifier<HLSLStaticModifier>())
- {
- if(auto constAttr = varDecl->findModifier<ConstModifier>())
- {
- // HLSL `static const` can be used as a constant expression
- if(auto initExpr = getInitExpr(m_astBuilder, varRef))
- {
- return TryConstantFoldExpr(initExpr);
- }
- }
- }
+ return tryConstantFoldDeclRef(varRef, circularityInfo);
}
else if(auto enumRef = declRef.as<EnumCaseDecl>())
{
// The cases in an `enum` declaration can also be used as constant expressions,
if(auto tagExpr = getTagExpr(m_astBuilder, enumRef))
{
- return TryConstantFoldExpr(tagExpr);
+ auto enumCaseDecl = enumRef.getDecl();
+ if(_checkForCircularityInConstantFolding(enumCaseDecl, circularityInfo))
+ return nullptr;
+
+ ConstantFoldingCircularityInfo newCircularityInfo(enumCaseDecl, circularityInfo);
+ return tryConstantFoldExpr(tagExpr, &newCircularityInfo);
}
}
}
if(auto castExpr = as<TypeCastExpr>(expr))
{
- auto val = TryConstantFoldExpr(castExpr->arguments[0]);
+ auto val = tryConstantFoldExpr(castExpr->arguments[0], circularityInfo);
if(val)
return val;
}
else if (auto invokeExpr = as<InvokeExpr>(expr))
{
- auto val = TryConstantFoldExpr(invokeExpr);
+ auto val = tryConstantFoldExpr(invokeExpr, circularityInfo);
if (val)
return val;
}
@@ -866,21 +904,18 @@ namespace Slang
return nullptr;
}
- IntVal* SemanticsVisitor::TryCheckIntegerConstantExpression(Expr* exp)
+ IntVal* SemanticsVisitor::tryFoldIntegerConstantExpression(
+ Expr* expr,
+ ConstantFoldingCircularityInfo* circularityInfo)
{
// Check if type is acceptable for an integer constant expression
- if(auto basicType = as<BasicExpressionType>(exp->type.type))
- {
- if(!isIntegerBaseType(basicType->baseType))
- return nullptr;
- }
- else
- {
+ //
+ if(!isScalarIntegerType(expr->type))
return nullptr;
- }
// Consider operations that we might be able to constant-fold...
- return TryConstantFoldExpr(exp);
+ //
+ return tryConstantFoldExpr(expr, circularityInfo);
}
IntVal* SemanticsVisitor::CheckIntegerConstantExpression(Expr* inExpr, DiagnosticSink* sink)
@@ -894,7 +929,7 @@ namespace Slang
// No need to issue further errors if the type coercion failed.
if(IsErrorExpr(expr)) return nullptr;
- auto result = TryCheckIntegerConstantExpression(expr);
+ auto result = tryFoldIntegerConstantExpression(expr, nullptr);
if (!result && sink)
{
sink->diagnose(expr, Diagnostics::expectedIntegerConstantNotConstant);
@@ -915,7 +950,7 @@ namespace Slang
// No need to issue further errors if the type coercion failed.
if(IsErrorExpr(expr)) return nullptr;
- auto result = TryConstantFoldExpr(expr);
+ auto result = tryConstantFoldExpr(expr, nullptr);
if (!result)
{
getSink()->diagnose(expr, Diagnostics::expectedIntegerConstantNotConstant);
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 72965e8c5..93ae87f40 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -491,6 +491,18 @@ namespace Slang
// Capture the "base" expression in case this is a member reference
Expr* GetBaseExpr(Expr* expr);
+ /// Validate a declaration to ensure that it doesn't introduce a circularly-defined constant
+ ///
+ /// Circular definition in a constant may lead to infinite looping or stack overflow in
+ /// the compiler, so it needs to be protected against.
+ ///
+ /// Note that this function does *not* protect against circular definitions in general,
+ /// and a program that indirectly initializes a global variable using its own value (e.g.,
+ /// by calling a function that indirectly reads the variable) will be allowed and then
+ /// exhibit undefined behavior at runtime.
+ ///
+ void _validateCircularVarDefinition(VarDeclBase* varDecl);
+
public:
bool ValuesAreEqual(
@@ -778,6 +790,9 @@ namespace Slang
bool isIntegerBaseType(BaseType baseType);
+ /// Is `type` a scalar integer type.
+ bool isScalarIntegerType(Type* type);
+
// Validate that `type` is a suitable type to use
// as the tag type for an `enum`
void validateEnumTagType(Type* type, SourceLoc const& loc);
@@ -827,15 +842,50 @@ namespace Slang
return getNamePool()->getName(text);
}
- IntVal* TryConstantFoldExpr(
- InvokeExpr* invokeExpr);
+ /// Helper type to detect and catch circular definitions when folding constants,
+ /// to prevent the compiler from going into infinite loops or overflowing the stack.
+ struct ConstantFoldingCircularityInfo
+ {
+ ConstantFoldingCircularityInfo(
+ Decl* decl,
+ ConstantFoldingCircularityInfo* next)
+ : decl(decl)
+ , next(next)
+ {}
+
+ /// A declaration whose value is contributing to the constant being folded
+ Decl* decl = nullptr;
+
+ /// The rest of the links in the chain of declarations being folded
+ ConstantFoldingCircularityInfo* next = nullptr;
+ };
+
+ /// Try to apply front-end constant folding to determine the value of `invokeExpr`.
+ IntVal* tryConstantFoldExpr(
+ InvokeExpr* invokeExpr,
+ ConstantFoldingCircularityInfo* circularityInfo);
+
+ /// Try to apply front-end constant folding to determine the value of `expr`.
+ IntVal* tryConstantFoldExpr(
+ Expr* expr,
+ ConstantFoldingCircularityInfo* circularityInfo);
- IntVal* TryConstantFoldExpr(
- Expr* expr);
+ bool _checkForCircularityInConstantFolding(
+ Decl* decl,
+ ConstantFoldingCircularityInfo* circularityInfo);
- // Try to check an integer constant expression, either returning the value,
- // or NULL if the expression isn't recognized as a constant.
- IntVal* TryCheckIntegerConstantExpression(Expr* exp);
+ /// Try to resolve a compile-time constant `IntVal` from the given `declRef`.
+ IntVal* tryConstantFoldDeclRef(
+ DeclRef<VarDeclBase> const& declRef,
+ ConstantFoldingCircularityInfo* circularityInfo);
+
+ /// Try to extract the value of an integer constant expression, either
+ /// returning the `IntVal` value, or null if the expression isn't recognized
+ /// as an integer constant.
+ ///
+ IntVal* tryFoldIntegerConstantExpression(
+ Expr* expr,
+ ConstantFoldingCircularityInfo* circularityInfo);
// Enforce that an expression resolves to an integer constant, and get its value
IntVal* CheckIntegerConstantExpression(Expr* inExpr);
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 41c674ddb..f6d38889b 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -299,6 +299,7 @@ DIAGNOSTIC(30301, Error, globalGenParamInGlobalScopeOnly, "'type_param' can only
// TODO: need to assign numbers to all these extra diagnostics...
DIAGNOSTIC(39999, Fatal, cyclicReference, "cyclic reference '$0'.")
DIAGNOSTIC(39999, Fatal, localVariableUsedBeforeDeclared, "local variable '$0' is being used before its declaration.")
+DIAGNOSTIC(39999, Error, variableUsedInItsOwnDefinition, "the initial-value expression for variable '$0' depends on the value of the variable itself")
// 304xx: generics
DIAGNOSTIC(30400, Error, genericTypeNeedsArgs, "generic type '$0' used without argument")