diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/slang-check-impl.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-check-overload.cpp | 71 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 2 |
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") |
