summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-impl.h10
-rw-r--r--source/slang/slang-check-overload.cpp71
-rw-r--r--source/slang/slang-diagnostic-defs.h2
3 files changed, 83 insertions, 0 deletions
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 46cc329a9..88f1e2975 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -2031,6 +2031,16 @@ namespace Slang
OverloadResolveContext& /*context*/,
OverloadCandidate const& /*candidate*/);
+ /// Check if the given `expr` refers to an `in` function
+ /// parameter, or part of one (through field reference, etc.).
+ ///
+ /// If the expression refers into a parameter, returns
+ /// the declaration of the parameter. Otherwise returns
+ /// null.
+ ///
+ ParamDecl* isReferenceIntoFunctionInputParameter(
+ Expr* expr);
+
// Create a witness that attests to the fact that `type`
// is equal to itself.
TypeEqualityWitness* createTypeEqualityWitness(
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index 8709ae763..c13e74f69 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -488,6 +488,58 @@ namespace Slang
return false;
}
+ ParamDecl* SemanticsVisitor::isReferenceIntoFunctionInputParameter(
+ Expr* inExpr)
+ {
+ auto expr = inExpr;
+ for (;;)
+ {
+ if (auto declRefExpr = as<DeclRefExpr>(expr))
+ {
+ auto declRef = declRefExpr->declRef;
+ if(auto paramDeclRef = declRef.as<ParamDecl>())
+ {
+ if (paramDeclRef.as<ModernParamDecl>())
+ {
+ // functions declared in our "modern" style (using
+ // the `func` keyword) never have mutable `in`
+ // parameters.
+ //
+ return nullptr;
+ }
+
+ if (paramDeclRef.getDecl()->findModifier<OutModifier>())
+ {
+ // Function parameters marked with `out`, `inout`,
+ // or `in out` are all mutable in a way where
+ // the result of mutations will be visible to the
+ // caller.
+ //
+ return nullptr;
+ }
+
+ // At this point we have an l-value decl-ref to a
+ // function parameter that is (implicitly or
+ // explicitly) declared `in`.
+ //
+ return paramDeclRef.getDecl();
+ }
+ }
+ else if (auto memberExpr = as<MemberExpr>(expr))
+ {
+ expr = memberExpr->baseExpression;
+ continue;
+ }
+ else if (auto indexExpr = as<IndexExpr>(expr))
+ {
+ expr = indexExpr->baseExpression;
+ continue;
+ }
+
+ return nullptr;
+ }
+ }
+
bool SemanticsVisitor::TryCheckOverloadCandidateDirections(
OverloadResolveContext& context,
OverloadCandidate const& candidate)
@@ -519,6 +571,25 @@ namespace Slang
}
return false;
}
+
+ // The parameters of functions declared using traditional/legacy
+ // syntax are currently exposed as mutable locals within the body
+ // of the relevant function. As such, it is legal to call `[mutating]`
+ // methods on such a function parameter. However, doing so is typically
+ // indicative of an error on the programmer's part.
+ //
+ // We will detect such cases here and issue a diagnostic that explains
+ // the situation.
+ //
+ if(context.baseExpr && context.mode == OverloadResolveContext::Mode::ForReal)
+ {
+ if(auto paramDecl = isReferenceIntoFunctionInputParameter(context.baseExpr))
+ {
+ getSink()->diagnose(context.loc, Diagnostics::mutatingMethodOnFunctionInputParameter,
+ funcDeclRef.getName(),
+ paramDecl->getName());
+ }
+ }
}
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index e2c00ec38..8f2413c51 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -301,6 +301,8 @@ DIAGNOSTIC(30064, Note, implicitCastUsedAsLValue, "argument was implicitly cast
DIAGNOSTIC(30065, Error, newCanOnlyBeUsedToInitializeAClass, "`new` can only be used to initialize a class")
DIAGNOSTIC(30066, Error, classCanOnlyBeInitializedWithNew, "a class can only be initialized by a `new` clause")
+DIAGNOSTIC(30067, Error, mutatingMethodOnFunctionInputParameter, "mutating method '$0' called on `in` parameter '$1'; changes will not be visible to caller. copy the parameter into a local variable if this behavior is intended")
+
DIAGNOSTIC(30100, Error, staticRefToNonStaticMember, "type '$0' cannot be used to refer to non-static member '$1'")
DIAGNOSTIC(30200, Error, redeclaration, "declaration of '$0' conflicts with existing declaration")