Skip to content

Commit

Permalink
Merge pull request #30 from opendlang/private-this
Browse files Browse the repository at this point in the history
Private this
  • Loading branch information
adamdruppe committed Jan 27, 2024
2 parents 6f22df7 + 356997e commit 3f77889
Show file tree
Hide file tree
Showing 22 changed files with 176 additions and 21 deletions.
24 changes: 24 additions & 0 deletions changelog/private-this.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Add experimental `private(this)` visibility attribute

The `private` visibility attribute only limits access to the current module.
While `class` encapsulation [can be ensured using package modules](https://dlang.org/blog/2018/11/06/lost-in-translation-encapsulation),
some users dislike having to put classes into individual files to accomplish this.

With the `-preview=privateThis` switch, member variables can now be marked invisible outside of the
`class`, `struct` or `union` it's declared in.
Note that this feature has not been accepted into the D language yet.

---
class C
{
private int x;
private(this) int y;
}

void main()
{
auto c = new C();
c.x++; // allowed
c.y++; // not allowed
}
---
16 changes: 6 additions & 10 deletions compiler/src/dmd/access.d
Original file line number Diff line number Diff line change
Expand Up @@ -236,33 +236,28 @@ bool checkAccess(Scope* sc, Package p)
* Check whether symbols `s` is visible in `mod`.
*
* Params:
* mod = lookup origin
* origin = lookup origin
* s = symbol to check for visibility
* Returns: true if s is visible in mod
*/
bool symbolIsVisible(Module mod, Dsymbol s)
bool symbolIsVisible(Dsymbol origin, Dsymbol s)
{
Module mod = origin.getAccessModule();

// should sort overloads by ascending visibility instead of iterating here
s = mostVisibleOverload(s);
final switch (s.visible().kind)
{
case Visibility.Kind.undefined: return true;
case Visibility.Kind.none: return false; // no access
case Visibility.Kind.privateThis: return origin == s.isMember2();
case Visibility.Kind.private_: return s.getAccessModule() == mod;
case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
case Visibility.Kind.protected_: return s.getAccessModule() == mod;
case Visibility.Kind.public_, Visibility.Kind.export_: return true;
}
}

/**
* Same as above, but determines the lookup module from symbols `origin`.
*/
bool symbolIsVisible(Dsymbol origin, Dsymbol s)
{
return symbolIsVisible(origin.getAccessModule(), s);
}

/**
* Same as above but also checks for protected symbols visible from scope `sc`.
* Used for qualified name lookup.
Expand Down Expand Up @@ -293,6 +288,7 @@ bool checkSymbolAccess(Scope *sc, Dsymbol s)
{
case Visibility.Kind.undefined: return true;
case Visibility.Kind.none: return false; // no access
case Visibility.Kind.privateThis: return sc.getStructClassScope() == s.isMember2();
case Visibility.Kind.private_: return sc._module == s.getAccessModule();
case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -6777,6 +6777,7 @@ struct ASTBase
{
undefined,
none,
privateThis,
private_,
package_,
protected_,
Expand Down Expand Up @@ -6830,6 +6831,8 @@ struct ASTBase
return null;
case Visibility.Kind.none:
return "none";
case Visibility.Kind.privateThis:
return "private(this)";
case Visibility.Kind.private_:
return "private";
case Visibility.Kind.package_:
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/cli.d
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,8 @@ dmd -cov -unittest myprog.d
"disallow unsound immutable conversions that were formerly incorrectly permitted"),
Feature("systemVariables", "systemVariables",
"disable access to variables marked '@system' from @safe code"),
Feature("privateThis", "privateThis",
"add `private(this)` visibility attribute, private to the class/struct/union instead of module"),
];
}

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/cppmanglewin.d
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,7 @@ void mangleVisibility(ref OutBuffer buf, Declaration d, string privProtDef)@safe
{
switch (d.visibility.kind)
{
case Visibility.Kind.privateThis:
case Visibility.Kind.private_:
buf.writeByte(privProtDef[0]);
break;
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,7 @@ version (IN_LLVM)
const bool doUnittests = global.params.parsingUnittestsRequired();
scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.allowPrivateThis = global.params.privateThis;
p.nextToken();
p.parseModuleDeclaration();
md = p.md;
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/dsymbol.d
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ struct Visibility
{
undefined,
none, // no access
privateThis, // only within aggregate
private_,
package_,
protected_,
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dmd/dsymbolsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2203,6 +2203,7 @@ else // !IN_LLVM
auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.allowPrivateThis = global.params.privateThis;
p.nextToken();

auto d = p.parseDeclDefs(0);
Expand Down Expand Up @@ -6288,6 +6289,10 @@ private extern(C++) class AddMemberVisitor : Visitor
visd.visibility.pkg = tmp ? tmp.isPackage() : null;
visd.pkg_identifiers = null;
}
if (visd.visibility.kind == Visibility.Kind.privateThis && sds.isModule())
{
.error(visd.loc, "%s cannot be used in global scope", visd.toPrettyChars(false));
}
if (visd.visibility.kind == Visibility.Kind.package_ && visd.visibility.pkg && sc._module)
{
Module m = sc._module;
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -7538,6 +7538,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.allowPrivateThis = global.params.privateThis;
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);

Expand Down
20 changes: 12 additions & 8 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,11 +435,12 @@ struct Visibility final
{
undefined = 0u,
none = 1u,
private_ = 2u,
package_ = 3u,
protected_ = 4u,
public_ = 5u,
export_ = 6u,
privateThis = 2u,
private_ = 3u,
package_ = 4u,
protected_ = 5u,
public_ = 6u,
export_ = 7u,
};

Kind kind;
Expand Down Expand Up @@ -7367,7 +7368,7 @@ struct Scope final
linkage((LINK)1u),
cppmangle((CPPMANGLE)0u),
inlining(),
visibility(Visibility((Visibility::Kind)5u, nullptr)),
visibility(Visibility((Visibility::Kind)6u, nullptr)),
explicitVisibility(),
stc(),
depdecl(),
Expand All @@ -7378,7 +7379,7 @@ struct Scope final
aliasAsg()
{
}
Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* sw = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tf = nullptr, ScopeGuardStatement* os = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Dsymbol* inunion = nullptr, bool nofree = false, bool inLoop = false, int32_t intypeof = 0, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)5u, nullptr), int32_t explicitVisibility = 0, uint64_t stc = 0LLU, DeprecatedDeclaration* depdecl = nullptr, uint32_t flags = 0u, UserAttributeDeclaration* userAttribDecl = nullptr, DocComment* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr) :
Scope(Scope* enclosing, Module* _module = nullptr, ScopeDsymbol* scopesym = nullptr, FuncDeclaration* func = nullptr, VarDeclaration* varDecl = nullptr, Dsymbol* parent = nullptr, LabelStatement* slabel = nullptr, SwitchStatement* sw = nullptr, Statement* tryBody = nullptr, TryFinallyStatement* tf = nullptr, ScopeGuardStatement* os = nullptr, Statement* sbreak = nullptr, Statement* scontinue = nullptr, ForeachStatement* fes = nullptr, Scope* callsc = nullptr, Dsymbol* inunion = nullptr, bool nofree = false, bool inLoop = false, int32_t intypeof = 0, VarDeclaration* lastVar = nullptr, ErrorSink* eSink = nullptr, Module* minst = nullptr, TemplateInstance* tinst = nullptr, CtorFlow ctorflow = CtorFlow(), AlignDeclaration* aligndecl = nullptr, CPPNamespaceDeclaration* namespace_ = nullptr, LINK linkage = (LINK)1u, CPPMANGLE cppmangle = (CPPMANGLE)0u, PragmaDeclaration* inlining = nullptr, Visibility visibility = Visibility((Visibility::Kind)6u, nullptr), int32_t explicitVisibility = 0, uint64_t stc = 0LLU, DeprecatedDeclaration* depdecl = nullptr, uint32_t flags = 0u, UserAttributeDeclaration* userAttribDecl = nullptr, DocComment* lastdc = nullptr, void* anchorCounts = nullptr, Identifier* prevAnchor = nullptr, AliasDeclaration* aliasAsg = nullptr) :
enclosing(enclosing),
_module(_module),
scopesym(scopesym),
Expand Down Expand Up @@ -8194,6 +8195,7 @@ struct Param final
bool fix16997;
FeatureState dtorFields;
FeatureState systemVariables;
bool privateThis;
CHECKENABLE useInvariants;
CHECKENABLE useIn;
CHECKENABLE useOut;
Expand Down Expand Up @@ -8270,6 +8272,7 @@ struct Param final
shortenedMethods(true),
fixImmutableConv(),
fix16997(true),
privateThis(),
useInvariants((CHECKENABLE)0u),
useIn((CHECKENABLE)0u),
useOut((CHECKENABLE)0u),
Expand Down Expand Up @@ -8309,7 +8312,7 @@ struct Param final
mapfile()
{
}
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 warnings = (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, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* >* imppath = nullptr, Array<const char* >* fileImppath = nullptr, _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 = {}) :
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 warnings = (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, bool privateThis = false, 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, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<const char* >* imppath = nullptr, Array<const char* >* fileImppath = nullptr, _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 = {}) :
obj(obj),
multiobj(multiobj),
trace(trace),
Expand Down Expand Up @@ -8351,6 +8354,7 @@ struct Param final
fix16997(fix16997),
dtorFields(dtorFields),
systemVariables(systemVariables),
privateThis(privateThis),
useInvariants(useInvariants),
useIn(useIn),
useOut(useOut),
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/globals.d
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ extern (C++) struct Param
FeatureState dtorFields; // destruct fields of partially constructed objects
// https://issues.dlang.org/show_bug.cgi?id=14246
FeatureState systemVariables; // limit access to variables marked @system from @safe code
bool privateThis; // add `private(this)`, limiting access to this class/struct/union

CHECKENABLE useInvariants = CHECKENABLE._default; // generate class invariant checks
CHECKENABLE useIn = CHECKENABLE._default; // generate precondition checks
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ struct Param
FeatureState dtorFields; // destruct fields of partially constructed objects
// https://issues.dlang.org/show_bug.cgi?id=14246
FeatureState systemVariables; // limit access to variables marked @system from @safe code
d_bool privateThis; // add `private(this)`, limiting access to this class/struct/union

CHECKENABLE useInvariants; // generate class invariant checks
CHECKENABLE useIn; // generate precondition checks
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/hdrgen.d
Original file line number Diff line number Diff line change
Expand Up @@ -3306,9 +3306,10 @@ extern (D) string visibilityToString(Visibility.Kind kind) nothrow pure @safe
{
with (Visibility.Kind)
{
immutable string[7] a = [
immutable string[8] a = [
none : "none",
private_ : "private",
privateThis: "private(this)",
package_ : "package",
protected_ : "protected",
public_ : "public",
Expand Down
21 changes: 19 additions & 2 deletions compiler/src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
}

bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed
bool allowPrivateThis = true;

/*********************
* Use this constructor for string mixins.
Expand Down Expand Up @@ -857,7 +858,22 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
}

case TOK.private_:
prot = AST.Visibility.Kind.private_;
if (peekNext() == TOK.leftParenthesis && peekNext2() == TOK.this_)
{
if (!allowPrivateThis)
{
error("use `-preview=privateThis` to enable usage of `private(this)`");
}
nextToken();
nextToken();
nextToken();
check(TOK.rightParenthesis);
prot = AST.Visibility.Kind.privateThis;
}
else
{
prot = AST.Visibility.Kind.private_;
}
goto Lprot;

case TOK.package_:
Expand Down Expand Up @@ -887,7 +903,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
pAttrs.visibility.kind = prot;
const attrloc = token.loc;

nextToken();
if (pAttrs.visibility.kind != AST.Visibility.Kind.privateThis)
nextToken();

// optional qualified package identifier to bind
// visibility to
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/statementsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -4999,6 +4999,7 @@ private Statements* flatten(Statement statement, Scope* sc)
auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.allowPrivateThis = global.params.privateThis;
p.nextToken();

auto a = new Statements();
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/tocvdebug.d
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ uint visibilityToCVAttr(Visibility.Kind vis) pure nothrow @safe @nogc

final switch (vis)
{
case Visibility.Kind.privateThis:
case Visibility.Kind.private_: attribute = 1; break;
case Visibility.Kind.package_: attribute = 2; break;
case Visibility.Kind.protected_: attribute = 2; break;
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -6046,6 +6046,7 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc)
auto locm = adjustLocForMixin(str, loc, global.params.mixinOut);
scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests);
p.transitionIn = global.params.v.vin;
p.allowPrivateThis = global.params.privateThis;
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);

Expand Down

0 comments on commit 3f77889

Please sign in to comment.