summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2020-07-24 18:12:41 -0700
committerGitHub <noreply@github.com>2020-07-24 18:12:41 -0700
commit87940a649e3b4f757905015de95225d57ec2f27c (patch)
tree189ec1c515bd1180822e8887817c0f235a51c617
parent261fe7587d7413070a4e0f29e1a1bb7b0d2b5429 (diff)
Fix bugs related to mutating implementations of interface methods (#1461)
There are two main bug fixes here: * We were failing to diagnose when code calls a `[mutating]` method on a value that doesn't support mutation (that is an r-value instead of an l-value). * We had a bug in the synthesis logic for interface requirements where we used the *result* type of the requirement in place of each of the *parameter* types. The second bug made synthesis often produce incorrect signatures with `void` parameters. The first bug meant that even though a `[mutating]` method should not be able to satisfy a non-`[mutating]` method (and we had code to enforce this for the "exact match" case), when we go on to try and synthesize a non-`[mutating]` method that satisfies the requirement by delegating to the user-written one, it would end up succeeding, because nothing was stopping a non-`[mutating]` method from calling a `[mutating]` one. In each case this code adds a fix and a test case to confirm it.
-rw-r--r--source/slang/slang-check-decl.cpp9
-rw-r--r--source/slang/slang-check-overload.cpp49
-rw-r--r--source/slang/slang-diagnostic-defs.h1
-rw-r--r--tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang42
-rw-r--r--tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang.expected6
-rw-r--r--tests/diagnostics/methods/mutating-method-on-rvalue.slang26
-rw-r--r--tests/diagnostics/methods/mutating-method-on-rvalue.slang.expected8
7 files changed, 136 insertions, 5 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 2879d7fa2..3cc0fd0f5 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -1576,7 +1576,7 @@ namespace Slang
//
auto synParamDecl = m_astBuilder->create<ParamDecl>();
synParamDecl->nameAndLoc = paramDeclRef.getDecl()->nameAndLoc;
- synParamDecl->type.type = resultType;
+ synParamDecl->type.type = paramType;
// We need to add the parameter as a child declaration of
// the method we are building.
@@ -1599,7 +1599,12 @@ namespace Slang
// if any.
//
ThisExpr* synThis = nullptr;
- if( !requiredMemberDeclRef.getDecl()->hasModifier<HLSLStaticModifier>() )
+ if( requiredMemberDeclRef.getDecl()->hasModifier<HLSLStaticModifier>() )
+ {
+ auto synStaticModifier = m_astBuilder->create<HLSLStaticModifier>();
+ synFuncDecl->modifiers.first = synStaticModifier;
+ }
+ else
{
// For a non-`static` requirement, we need a `this` parameter.
//
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index 69baf9f75..0a1294965 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -358,11 +358,54 @@ namespace Slang
return true;
}
+ bool isEffectivelyMutating(CallableDecl* decl)
+ {
+ if(decl->hasModifier<MutatingAttribute>())
+ return true;
+
+ if(decl->hasModifier<NonmutatingAttribute>())
+ return false;
+
+ if(as<SetterDecl>(decl))
+ return true;
+
+ return false;
+ }
+
bool SemanticsVisitor::TryCheckOverloadCandidateDirections(
- OverloadResolveContext& /*context*/,
- OverloadCandidate const& /*candidate*/)
+ OverloadResolveContext& context,
+ OverloadCandidate const& candidate)
{
- // TODO(tfoley): check `in` and `out` markers, as needed.
+ if(candidate.flavor != OverloadCandidate::Flavor::Func)
+ return true;
+
+ auto funcDeclRef = candidate.item.declRef.as<CallableDecl>();
+ SLANG_ASSERT(funcDeclRef);
+
+ // Note: This operation was originally introduced as
+ // a place to add checking around l-value-ness of arguments
+ // and parameters, but currently that checking is being
+ // done in other places.
+ //
+ // For now we will only use this step to check the
+ // mutability of the `this` parameter where necessary.
+ //
+ if(!isEffectivelyStatic(funcDeclRef.getDecl()))
+ {
+ if(isEffectivelyMutating(funcDeclRef.getDecl()))
+ {
+ if(context.baseExpr && !context.baseExpr->type.isLeftValue)
+ {
+ if(context.mode == OverloadResolveContext::Mode::ForReal)
+ {
+ getSink()->diagnose(context.loc, Diagnostics::mutatingMethodOnImmutableValue, funcDeclRef.getName());
+ maybeDiagnoseThisNotLValue(context.baseExpr);
+ }
+ return false;
+ }
+ }
+ }
+
return true;
}
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 4ad449b54..4ef2aa7be 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -248,6 +248,7 @@ DIAGNOSTIC(30041, Error, bitOperationNonIntegral, "bit operation: operand must b
DIAGNOSTIC(30047, Error, argumentExpectedLValue, "argument passed to parameter '$0' must be l-value.")
DIAGNOSTIC(30048, Note, implicitCastUsedAsLValue, "argument was implicitly cast from '$0' to '$1', and Slang does not support using an implicit cast as an l-value")
DIAGNOSTIC(30049, Note, thisIsImmutableByDefault, "a 'this' parameter is an immutable parameter by default in Slang; apply the `[mutating]` attribute to the function declaration to opt in to a mutable `this`")
+DIAGNOSTIC(30050, Error, mutatingMethodOnImmutableValue, "mutating method '$0' cannot be called on an immutable value")
DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$0'")
DIAGNOSTIC(30052, Error, invalidSwizzleExpr, "invalid swizzle pattern '$0' on type '$1'")
diff --git a/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang b/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang
new file mode 100644
index 000000000..2dbe45ccb
--- /dev/null
+++ b/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang
@@ -0,0 +1,42 @@
+// mutating-impl-of-non-mutating-req.slang
+
+//DIAGNOSTIC_TEST:SIMPLE:-target hlsl -entry main
+
+interface IThing
+{
+ int processValue(int inValue);
+}
+
+struct Counter : IThing
+{
+ int state;
+
+ [mutating] int processValue(int inValue)
+ {
+ int result = state;
+ state += inValue;
+ return state;
+ }
+}
+
+int helper<T : IThing>(T thing, int value)
+{
+ return thing.processValue(value);
+}
+
+int test(int value)
+{
+ Counter counter = { value };
+ return helper(counter, value);
+}
+
+cbuffer C
+{
+ int gValue;
+}
+
+[shader("fragment")]
+int main() : SV_Target
+{
+ return test(gValue);
+} \ No newline at end of file
diff --git a/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang.expected b/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang.expected
new file mode 100644
index 000000000..922a6c826
--- /dev/null
+++ b/tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang.expected
@@ -0,0 +1,6 @@
+result code = -1
+standard error = {
+tests/diagnostics/interfaces/mutating-impl-of-non-mutating-req.slang(10): error 38100: type 'Counter' does not provide required interface member 'processValue'
+}
+standard output = {
+}
diff --git a/tests/diagnostics/methods/mutating-method-on-rvalue.slang b/tests/diagnostics/methods/mutating-method-on-rvalue.slang
new file mode 100644
index 000000000..ab51244aa
--- /dev/null
+++ b/tests/diagnostics/methods/mutating-method-on-rvalue.slang
@@ -0,0 +1,26 @@
+// mutating-method-on-rvalue.slang
+
+//DIAGNOSTIC_TEST:SIMPLE:-target hlsl -entry main
+
+struct Counter
+{
+ int count;
+
+ [mutating] void increment() { count++; }
+
+ void bad()
+ {
+ increment();
+ }
+}
+
+cbuffer C
+{
+ Counter gCounter;
+}
+
+[shader("compute")]
+void main()
+{
+ gCounter.increment();
+} \ No newline at end of file
diff --git a/tests/diagnostics/methods/mutating-method-on-rvalue.slang.expected b/tests/diagnostics/methods/mutating-method-on-rvalue.slang.expected
new file mode 100644
index 000000000..878882bd3
--- /dev/null
+++ b/tests/diagnostics/methods/mutating-method-on-rvalue.slang.expected
@@ -0,0 +1,8 @@
+result code = -1
+standard error = {
+tests/diagnostics/methods/mutating-method-on-rvalue.slang(13): error 30050: mutating method 'increment' cannot be called on an immutable value
+tests/diagnostics/methods/mutating-method-on-rvalue.slang(13): note 30049: a 'this' parameter is an immutable parameter by default in Slang; apply the `[mutating]` attribute to the function declaration to opt in to a mutable `this`
+tests/diagnostics/methods/mutating-method-on-rvalue.slang(25): error 30050: mutating method 'increment' cannot be called on an immutable value
+}
+standard output = {
+}