diff --git a/changelog/dmd.safer.dd b/changelog/dmd.safer.dd new file mode 100644 index 000000000000..3ab3229b494c --- /dev/null +++ b/changelog/dmd.safer.dd @@ -0,0 +1,3 @@ +This adds `-preview=safer` which enables safety checking on un-attributed functions. + +For more information, see: [safer.md](https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md) diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index e721a2c798d7..70fa4b08d785 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -972,6 +972,9 @@ dmd -cov -unittest myprog.d Feature("rvaluerefparam", "rvalueRefParam", "enable rvalue arguments to ref parameters", "https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a"), + Feature("safer", "safer", + "more safety checks by default", + "https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md", true, false), Feature("nosharedaccess", "noSharedAccess", "disable access to shared memory objects", "https://dlang.org/spec/const3.html#shared"), diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index 173532af397c..918863b56801 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -1137,8 +1137,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // __swap variable, which can be trusted if (dsym.type.hasPointers()) // also computes type size - sc.setUnsafe(false, dsym.loc, - "`void` initializers for pointers not allowed in safe functions"); + { + if (0 && isSaferD(sc.func)) + error(dsym.loc, "`void` initializers for pointers not allowed by default, remove `= void` for default initialization or annotate with `@trusted` or `@system`"); + else + sc.setUnsafe(false, dsym.loc, + "`void` initializers for pointers not allowed in safe functions"); + } else if (dsym.type.hasInvariant()) sc.setUnsafe(false, dsym.loc, "`void` initializers for structs with invariants are not allowed in safe functions"); diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index e7cdf7e9e065..a77f064576d8 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -8252,6 +8252,7 @@ struct Param final FeatureState fieldwise; bool fixAliasThis; FeatureState rvalueRefParam; + FeatureState safer; FeatureState noSharedAccess; bool previewIn; bool inclusiveInContracts; @@ -8386,7 +8387,7 @@ struct Param final timeTraceFile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array imppath = Array(), Array fileImppath = Array(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, uint32_t versionlevel = 0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) : obj(obj), multiobj(multiobj), trace(trace), @@ -8420,6 +8421,7 @@ struct Param final fieldwise(fieldwise), fixAliasThis(fixAliasThis), rvalueRefParam(rvalueRefParam), + safer(safer), noSharedAccess(noSharedAccess), previewIn(previewIn), inclusiveInContracts(inclusiveInContracts), diff --git a/compiler/src/dmd/globals.d b/compiler/src/dmd/globals.d index a8a8afd2abbf..88b27d20bb54 100644 --- a/compiler/src/dmd/globals.d +++ b/compiler/src/dmd/globals.d @@ -188,6 +188,8 @@ extern (C++) struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md FeatureState noSharedAccess; // read/write access to shared memory objects bool previewIn; // `in` means `[ref] scope const`, accepts rvalues bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract diff --git a/compiler/src/dmd/globals.h b/compiler/src/dmd/globals.h index c1ee8807ad68..c5659ea10b62 100644 --- a/compiler/src/dmd/globals.h +++ b/compiler/src/dmd/globals.h @@ -191,6 +191,9 @@ struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md + FeatureState noSharedAccess; // read/write access to shared memory objects d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract diff --git a/compiler/src/dmd/safe.d b/compiler/src/dmd/safe.d index 9f70db4fd595..86dff7b5bcf7 100644 --- a/compiler/src/dmd/safe.d +++ b/compiler/src/dmd/safe.d @@ -27,7 +27,7 @@ import dmd.errors; import dmd.expression; import dmd.func; import dmd.funcsem : isRootTraitsCompilesScope; -import dmd.globals : FeatureState; +import dmd.globals : FeatureState, global; import dmd.id; import dmd.identifier; import dmd.location; @@ -317,6 +317,16 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) return false; } +/************************************** + * Safer D adds safety checks to functions with the default + * trust setting. + */ +bool isSaferD(FuncDeclaration fd) +{ + return fd.type.toTypeFunction().trust == TRUST.default_ && + global.params.safer == FeatureState.enabled; +} + bool isSafe(FuncDeclaration fd) { if (fd.safetyInprocess) @@ -365,7 +375,7 @@ extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc, } } } - else if (fd.isSafe()) + else if (fd.isSafe() || fd.isSaferD()) { if (!gag && format) .error(loc, format, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); @@ -393,7 +403,7 @@ extern (D) bool setFunctionToUnsafe(FuncDeclaration fd) setFunctionToUnsafe(fd.fes.func); return true; } - else if (fd.isSafe()) + else if (fd.isSafe() || fd.isSaferD()) return true; return false; } diff --git a/compiler/src/dmd/statementsem.d b/compiler/src/dmd/statementsem.d index d11fce000928..8138bd2eb952 100644 --- a/compiler/src/dmd/statementsem.d +++ b/compiler/src/dmd/statementsem.d @@ -57,7 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; -import dmd.safe : isSafe, setUnsafe; +import dmd.safe : isSafe, isSaferD, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; @@ -1987,7 +1987,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } ss.hasDefault = sc.sw.sdefault || - !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe); + !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe || sc.func.isSaferD); if (!ss.hasDefault) { if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile) diff --git a/compiler/test/compilable/previewhelp.d b/compiler/test/compilable/previewhelp.d index 14d5781964e1..10a780c56677 100644 --- a/compiler/test/compilable/previewhelp.d +++ b/compiler/test/compilable/previewhelp.d @@ -13,6 +13,7 @@ Upcoming language changes listed by -preview=name: =fieldwise use fieldwise comparisons for struct equality (https://dlang.org/changelog/2.085.0.html#no-cmpsb) =fixAliasThis when a symbol is resolved, check alias this scope before going to upper scopes (https://github.com/dlang/dmd/pull/8885) =rvaluerefparam enable rvalue arguments to ref parameters (https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a) + =safer more safety checks by default (https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md) =nosharedaccess disable access to shared memory objects (https://dlang.org/spec/const3.html#shared) =in `in` on parameters means `scope const [ref]` and accepts rvalues (https://dlang.org/spec/function.html#in-params) =inclusiveincontracts 'in' contracts of overridden methods must be a superset of parent contract (https://dlang.org/changelog/2.095.0.html#inclusive-incontracts) diff --git a/compiler/test/fail_compilation/safer.d b/compiler/test/fail_compilation/safer.d new file mode 100644 index 000000000000..a7f260a28489 --- /dev/null +++ b/compiler/test/fail_compilation/safer.d @@ -0,0 +1,18 @@ +/* REQUIRED_ARGS: -preview=safer +TEST_OUTPUT: +--- +fail_compilation/safer.d(10): Error: `void` initializers for pointers not allowed in safe functions +--- +*/ + +void test1() +{ + int* p = void; +} + +void foo3() { } + +void test2() +{ + foo3(); // should not be an error +}