diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d index c584b84648..d11fce0009 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -779,14 +779,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors - /* Check for inference errors - */ + /* Check for inference errors and apply mutability checks inline */ if (!inferApplyArgTypes(fs, sc, sapply)) { - /** - Try and extract the parameter count of the opApply callback function, e.g.: - int opApply(int delegate(int, float)) => 2 args - */ bool foundMismatch = false; size_t foreachParamCount = 0; if (sapplyOld) @@ -806,6 +801,19 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto tf = fparam.type.nextOf().isTypeFunction(); foreachParamCount = tf.parameterList.length; foundMismatch = true; + + // Mutability check + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } } } } @@ -824,6 +832,24 @@ Statement statementSemanticVisit(Statement s, Scope* sc) return setError(); } + // If inference succeeds, proceed with post-checks + if (sapply && sapply.isFuncDeclaration()) + { + FuncDeclaration fd = sapply.isFuncDeclaration(); + + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } + } + Type tab = fs.aggr.type.toBasetype(); if (tab.ty == Ttuple) // don't generate new scope for tuple loops diff --git a/compiler/test/fail_compilation/test24353.d b/compiler/test/fail_compilation/test24353.d new file mode 100644 index 0000000000..76174aee62 --- /dev/null +++ b/compiler/test/fail_compilation/test24353.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=24353 + +/** +TEST_OUTPUT: +--- +fail_compilation/test24353.d(23): Error: mutable method `test24353.S.opApply` is not callable using a `const` object +fail_compilation/test24353.d(14): Consider adding `const` or `inout` here +--- +*/ + + +struct S +{ + int opApply(int delegate(int) dg) + { + return 0; + } +} + +void example() +{ + const S s; + foreach (e; s) {} // Error expected here +}