Skip to content

Commit

Permalink
Implement Safer D (#17044)
Browse files Browse the repository at this point in the history
  • Loading branch information
WalterBright authored Nov 28, 2024
1 parent 519d388 commit 347c883
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 6 deletions.
3 changes: 3 additions & 0 deletions changelog/dmd.safer.dd
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 3 additions & 0 deletions compiler/src/dmd/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -8252,6 +8252,7 @@ struct Param final
FeatureState fieldwise;
bool fixAliasThis;
FeatureState rvalueRefParam;
FeatureState safer;
FeatureState noSharedAccess;
bool previewIn;
bool inclusiveInContracts;
Expand Down Expand Up @@ -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<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* > imppath = Array<const char* >(), Array<const char* > fileImppath = Array<const char* >(), _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<const char* > runargs = Array<const char* >(), Array<const char* > cppswitches = Array<const char* >(), const char* cpp = nullptr, Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _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<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* > imppath = Array<const char* >(), Array<const char* > fileImppath = Array<const char* >(), _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<const char* > runargs = Array<const char* >(), Array<const char* > cppswitches = Array<const char* >(), const char* cpp = nullptr, Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _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),
Expand Down Expand Up @@ -8420,6 +8421,7 @@ struct Param final
fieldwise(fieldwise),
fixAliasThis(fixAliasThis),
rvalueRefParam(rvalueRefParam),
safer(safer),
noSharedAccess(noSharedAccess),
previewIn(previewIn),
inclusiveInContracts(inclusiveInContracts),
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/globals.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dmd/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions compiler/src/dmd/safe.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() : "");
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dmd/statementsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/test/compilable/previewhelp.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions compiler/test/fail_compilation/safer.d
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 347c883

Please sign in to comment.