Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow non-reference-type traits #4137

Merged
merged 47 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e861001
Parsing and resolution
RustanLeino Jun 5, 2023
73b6431
Add verification support
RustanLeino Jun 5, 2023
952a34b
Adjust error message wording in .expect files
RustanLeino Jun 5, 2023
b0d07f2
Adjust .expect files for verification messages
RustanLeino Jun 5, 2023
d990ba9
Add compilation support
RustanLeino Jun 5, 2023
f961c73
Fix up recently added coersions
RustanLeino Jun 6, 2023
23e9172
Add new tests
RustanLeino Jun 6, 2023
21438a7
Add tests for issue 4046
RustanLeino Jun 6, 2023
b9f4f39
Fix whitespace
RustanLeino Jun 6, 2023
3e484c0
Update options doc
RustanLeino Jun 6, 2023
b91ffee
Adjust wording in expected error in tutorial
RustanLeino Jun 8, 2023
3e688c9
Update Go runtime with new generated code
RustanLeino Jun 8, 2023
cefc6de
Merge branch 'master' into non-reference-traits
RustanLeino Jun 8, 2023
4883fb3
Correct spelling
RustanLeino Jun 8, 2023
1c15a01
Remove p_no_mutable_fields_in_value_types
RustanLeino Jun 8, 2023
3482ada
Add test for p_general_traits_beta
RustanLeino Jun 8, 2023
43ef4ff
Merge branch 'master' into non-reference-traits
RustanLeino Jun 8, 2023
832ee5e
Fix error-catalog template
RustanLeino Jun 8, 2023
6cc241d
Merge branch 'master' into non-reference-traits
RustanLeino Jun 8, 2023
6a4bac2
Fix merge
RustanLeino Jun 8, 2023
9687c6f
Fix newtype-with-trait printing in Python, per reveiw comments
RustanLeino Jun 22, 2023
b8de6e0
chore: Parameter renaming to simplify merge
RustanLeino Jun 29, 2023
11d65c2
Merge branch 'master' into non-reference-traits
RustanLeino Jun 29, 2023
60af01f
Fix merge
RustanLeino Jun 29, 2023
6279117
Fix testDafnyForEachCompiler violations
RustanLeino Jun 29, 2023
cd341cf
Find moduleInfo elsewhere
RustanLeino Jun 29, 2023
05f6742
Merge branch 'master' into non-reference-traits
RustanLeino Jun 30, 2023
6ccf0fd
Merge branch 'master' into non-reference-traits
RustanLeino Jun 30, 2023
9d5baf2
Report C++’s lack of trait support to TestDafny
RustanLeino Jun 30, 2023
bdad171
Speed up test
RustanLeino Jun 30, 2023
408efd0
Merge branch 'master' into non-reference-traits
RustanLeino Jul 1, 2023
086b484
Fix merge
RustanLeino Jul 1, 2023
e17b252
Update docs/HowToFAQ/Errors-Parser.template
RustanLeino Jul 3, 2023
a835cde
Update docs/DafnyRef/Options.txt
RustanLeino Jul 3, 2023
fa2f8a4
Update Test/traits/NonReferenceTraitsCompile.dfy
RustanLeino Jul 3, 2023
ef5b66e
Update Test/traits/NonReferenceTraits.dfy
RustanLeino Jul 3, 2023
9213570
Update Source/DafnyCore/AST/Modules/ModuleDefinition.cs
RustanLeino Jul 3, 2023
4a900e7
Update Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameR…
RustanLeino Jul 3, 2023
c88fb15
Update Source/DafnyCore/Compilers/Java/Compiler-java.cs
RustanLeino Jul 3, 2023
f21d7c1
Update Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameR…
RustanLeino Jul 3, 2023
dc33d45
Act on PR comments
RustanLeino Jul 3, 2023
4c87084
Merge branch 'master' into non-reference-traits
RustanLeino Jul 3, 2023
1efca99
Change wording in error message
RustanLeino Jul 3, 2023
023f848
Adjust line numbers in .expect file
RustanLeino Jul 3, 2023
148f93b
Make test what it what meant to be
RustanLeino Jul 3, 2023
5cd1185
Fix message in error-message testing (parsing)
RustanLeino Jul 3, 2023
c0e4279
Account for changed wording in error messages
RustanLeino Jul 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Source/DafnyCore/AST/Cloner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne

if (d is AbstractTypeDecl) {
var dd = (AbstractTypeDecl)d;
return new AbstractTypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, CloneTPChar(dd.Characteristics), dd.TypeArgs.ConvertAll(CloneTypeParam), dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
return new AbstractTypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent,
CloneTPChar(dd.Characteristics), dd.TypeArgs.ConvertAll(CloneTypeParam),
dd.ParentTraits.ConvertAll(CloneType),
dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
} else if (d is SubsetTypeDecl) {
Contract.Assume(!(d is NonNullTypeDecl)); // don't clone the non-null type declaration; close the class, which will create a new non-null type declaration
var dd = (SubsetTypeDecl)d;
Expand All @@ -83,10 +86,12 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne
var dd = (NewtypeDecl)d;
if (dd.Var == null) {
return new NewtypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, CloneType(dd.BaseType),
dd.ParentTraits.ConvertAll(CloneType),
dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
} else {
return new NewtypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, CloneBoundVar(dd.Var, false),
CloneExpr(dd.Constraint), dd.WitnessKind, CloneExpr(dd.Witness),
dd.ParentTraits.ConvertAll(CloneType),
dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
}
} else if (d is TupleTypeDecl) {
Expand All @@ -97,13 +102,17 @@ public virtual TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition ne
var dd = (IndDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
var dt = new IndDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors, dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
var dt = new IndDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors,
dd.ParentTraits.ConvertAll(CloneType),
dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
return dt;
} else if (d is CoDatatypeDecl) {
var dd = (CoDatatypeDecl)d;
var tps = dd.TypeArgs.ConvertAll(CloneTypeParam);
var ctors = dd.Ctors.ConvertAll(CloneCtor);
var dt = new CoDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors, dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
var dt = new CoDatatypeDecl(Range(dd.RangeToken), dd.NameNode.Clone(this), newParent, tps, ctors,
dd.ParentTraits.ConvertAll(CloneType),
dd.Members.ConvertAll(d => CloneMember(d, false)), CloneAttributes(dd.Attributes), dd.IsRefining);
return dt;
} else if (d is IteratorDecl) {
var dd = (IteratorDecl)d;
Expand Down
15 changes: 6 additions & 9 deletions Source/DafnyCore/AST/Grammar/ParseErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public enum ErrorId {
p_module_level_function_always_static,
p_module_level_method_always_static,
p_bad_datatype_refinement,
p_no_mutable_fields_in_value_types,
p_module_level_const_always_static,
p_const_decl_missing_identifier,
p_bad_const_initialize_op,
Expand Down Expand Up @@ -138,7 +137,7 @@ public enum ErrorId {
p_deprecated_statement_refinement,
p_internal_exception,
p_file_has_no_code,

p_general_traits_beta,
}

static ParseErrors() {
Expand All @@ -152,6 +151,11 @@ static ParseErrors() {
Add(ErrorId.p_abstract_not_allowed,
@"
Only modules may be declared abstract.
", Remove(true));

Add(ErrorId.p_general_traits_beta,
@"
Use of traits as non-reference types is a beta feature. To engage, use /generalTraits:1.
", Remove(true));

Add(ErrorId.p_no_ghost_for_by_method,
Expand Down Expand Up @@ -299,13 +303,6 @@ This mistake is easy to make when the clauses are on the same line.
It is only allowed to add members to the body of the datatype.
");

Add(ErrorId.p_no_mutable_fields_in_value_types,
@"
The `var` declaration declares a mutable field, which is only permitted within
classes, traits and iterators.
`const` declarations can be members of value-types, such as datatypes.
", Replace("const"));

Add(ErrorId.p_module_level_function_always_static,
@"
All names declared in a module (outside a class-like entity) are implicitly `static`.
Expand Down
28 changes: 20 additions & 8 deletions Source/DafnyCore/AST/Grammar/Printer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ public void PrintTopLevelDecls(CompilationData compilation, IEnumerable<TopLevel
if (i++ != 0) { wr.WriteLine(); }
Indent(indent);
PrintClassMethodHelper("type", at.Attributes, at.Name + TPCharacteristicsSuffix(at.Characteristics), d.TypeArgs);
PrintExtendsClause(at);
if (at.Members.Count == 0) {
wr.WriteLine();
} else {
Expand All @@ -290,6 +291,7 @@ public void PrintTopLevelDecls(CompilationData compilation, IEnumerable<TopLevel
if (i++ != 0) { wr.WriteLine(); }
Indent(indent);
PrintClassMethodHelper("newtype", dd.Attributes, dd.Name, new List<TypeParameter>());
PrintExtendsClause(dd);
wr.Write(" = ");
if (dd.Var == null) {
PrintType(dd.BaseType);
Expand Down Expand Up @@ -365,6 +367,11 @@ public void PrintTopLevelDecls(CompilationData compilation, IEnumerable<TopLevel
if (i++ != 0) { wr.WriteLine(); }
PrintClass(cl, indent, fileBeingPrinted);

} else if (d is ClassLikeDecl) {
var cl = (ClassLikeDecl)d;
if (i++ != 0) { wr.WriteLine(); }
PrintClass(cl, indent, fileBeingPrinted);

} else if (d is ValuetypeDecl) {
var vtd = (ValuetypeDecl)d;
if (i++ != 0) { wr.WriteLine(); }
Expand Down Expand Up @@ -604,7 +611,7 @@ void PrintIteratorSignature(IteratorDecl iter, int indent) {
Indent(indent);
PrintClassMethodHelper("iterator", iter.Attributes, iter.Name, iter.TypeArgs);
if (iter.IsRefining) {
wr.Write(" ... ");
wr.Write(" ...");
} else {
PrintFormals(iter.Ins, iter);
if (iter.Outs.Count != 0) {
Expand Down Expand Up @@ -650,14 +657,9 @@ public void PrintClass(ClassLikeDecl c, int indent, string fileBeingPrinted) {
Indent(indent);
PrintClassMethodHelper((c is TraitDecl) ? "trait" : "class", c.Attributes, c.Name, c.TypeArgs);
if (c.IsRefining) {
wr.Write(" ... ");
wr.Write(" ...");
} else {
string sep = " extends ";
foreach (var trait in c.ParentTraits) {
wr.Write(sep);
PrintType(trait);
sep = ", ";
}
PrintExtendsClause(c);
}

if (c.Members.Count == 0) {
Expand All @@ -680,6 +682,15 @@ public void PrintClass(ClassLikeDecl c, int indent, string fileBeingPrinted) {
}
}

private void PrintExtendsClause(TopLevelDeclWithMembers c) {
string sep = " extends ";
foreach (var trait in c.ParentTraits) {
wr.Write(sep);
PrintType(trait);
sep = ", ";
}
}

public void PrintMembers(List<MemberDecl> members, int indent, string fileBeingPrinted) {
Contract.Requires(members != null);

Expand Down Expand Up @@ -805,6 +816,7 @@ public void PrintDatatype(DatatypeDecl dt, int indent, string fileBeingPrinted)
Contract.Requires(dt != null);
Indent(indent);
PrintClassMethodHelper(dt is IndDatatypeDecl ? "datatype" : "codatatype", dt.Attributes, dt.Name, dt.TypeArgs);
PrintExtendsClause(dt);
wr.Write(" =");
string sep = "";
foreach (DatatypeCtor ctor in dt.Ctors) {
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/AST/Grammar/ProgramParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ public static void AddParseResultToProgram(DfyParseResult parseResult, Program p
literalModuleDecl.ModuleDef.EnclosingModule = defaultModule;
}

if (declToMove is ClassLikeDecl classDecl) {
classDecl.NonNullTypeDecl.EnclosingModuleDefinition = defaultModule;
if (declToMove is ClassLikeDecl { NonNullTypeDecl: { } nonNullTypeDecl }) {
nonNullTypeDecl.EnclosingModuleDefinition = defaultModule;
}
if (declToMove is DefaultClassDecl defaultClassDecl) {
foreach (var member in defaultClassDecl.Members) {
Expand Down
118 changes: 113 additions & 5 deletions Source/DafnyCore/AST/Modules/ModuleDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,9 @@ public ModuleSignature RegisterTopLevelDecls(ModuleResolver resolver, bool useIm
registerThisDecl = nntd;
registerUnderThisName = d.Name;
} else {
// Register each class and trait C under its own name, C. Below, we will change this for reference types (which includes all classes
// and some of the traits), so that C? maps to the class/trait and C maps to the corresponding NonNullTypeDecl. We will need these
// initial mappings in order to look through the parent traits of traits, below.
registerThisDecl = d;
registerUnderThisName = d.Name;
}
Expand Down Expand Up @@ -737,16 +740,22 @@ public ModuleSignature RegisterTopLevelDecls(ModuleResolver resolver, bool useIm
}
}

// Now, for each class, register its possibly-null type
DetermineReferenceTypes(resolver, sig);

// Now, for each reference type (class and some traits), register its possibly-null type.
// In the big loop above, each class and trait was registered under its own name. We're now going to change that for the reference types.
foreach (TopLevelDecl d in TopLevelDecls) {
if ((d as ClassLikeDecl)?.NonNullTypeDecl != null) {
if (d is ClassLikeDecl { NonNullTypeDecl: { } nntd }) {
var name = d.Name + "?";
TopLevelDecl prev;
if (toplevels.TryGetValue(name, out prev)) {
if (toplevels.ContainsKey(name)) {
resolver.reporter.Error(MessageSource.Resolver, d,
"a module that already contains a top-level declaration '{0}' is not allowed to declare a {1} '{2}'",
"a module that already contains a top-level declaration '{0}' is not allowed to declare a reference type ({1}) '{2}'",
name, d.WhatKind, d.Name);
} else {
// change the mapping of d.Name to d.NonNullTypeDecl
toplevels[d.Name] = nntd;
sig.TopLevels[d.Name] = nntd;
// map the name d.Name+"?" to d
toplevels[name] = d;
sig.TopLevels[name] = d;
}
Expand All @@ -755,4 +764,103 @@ public ModuleSignature RegisterTopLevelDecls(ModuleResolver resolver, bool useIm

return sig;
}

private void DetermineReferenceTypes(ModuleResolver resolver, ModuleSignature sig) {
// Figure out which TraitDecl's are reference types, and for each of them, create a corresponding NonNullTypeDecl.
RustanLeino marked this conversation as resolved.
Show resolved Hide resolved
// To figure this out, we need to look at the parents of each TraitDecl, but those parents have not yet been resolved.
// Since we just need the head of each parent, we'll do that name resolution here (and will redo it later, when each parent
// type is resolved properly).
//
// Some inaccuracies can occur here, since possibly-null types have not yet been registered. However, since such types aren't allowed
// as parents, it doesn't matter that they aren't available yet.
//
// If the head of a parent trait cannot be resolved, it is ignored here. An error will be reported later, when trait declarations are
// resolved properly. Similarly, any cycle detected among the trait-parent heads is ignored. Cycles are detected (again) later and an
// error will be reported then (in the meantime, we may have computed incorrectly whether or not a TraitDecl is a reference type, but
// the cycle still remains, so an error will be reported later). (Btw, the later cycle detection detects not only cycles among parent
// heads, but also among the type arguments of parent traits.)
//
// In the following dictionary, a TraitDecl not being present among the keys means it has not been visited in the InheritsFromObject traversal.
// If a TraitDecl is a key and maps to "false", then it is currently being visited.
// If a TraitDecl is a key and maps to "true", then its .IsReferenceTypeDecl has been computed and is ready to be used.
var traitsProgress = new Dictionary<TraitDecl, bool>();
foreach (var decl in TopLevelDecls.Where(d => d is TraitDecl)) {
// Resolve a "path" to a top-level declaration, if possible. On error, return null.
// The path is expected to consist of NameSegment or ExprDotName nodes.
TopLevelDecl ResolveNamePath(Expression path) {
// A single NameSegment is a little different, because it may refer to built-in type (of interest here: "object").
if (path is NameSegment nameSegment) {
if (sig.TopLevels.TryGetValue(nameSegment.Name, out var topLevelDecl)) {
return topLevelDecl;
} else if (resolver.moduleInfo != null && resolver.moduleInfo.TopLevels.TryGetValue(nameSegment.Name, out topLevelDecl)) {
// For "object" and other reference-type declarations from other modules, we're picking up the NonNullTypeDecl; if so, return
// the original declaration.
return topLevelDecl is NonNullTypeDecl nntd ? nntd.ViewAsClass : topLevelDecl;
} else if (resolver.ProgramResolver.SystemModuleManager.systemNameInfo.TopLevels.TryGetValue(nameSegment.Name, out topLevelDecl)) {
// For "object" and other reference-type declarations from other modules, we're picking up the NonNullTypeDecl; if so, return
// the original declaration.
return topLevelDecl is NonNullTypeDecl nntd ? nntd.ViewAsClass : topLevelDecl;
} else {
return null;
}
}

// convert the ExprDotName to a list of strings
var names = new List<string>();
while (path is ExprDotName exprDotName) {
names.Add(exprDotName.SuffixName);
path = exprDotName.Lhs;
}
names.Add(((NameSegment)path).Name);
var s = sig;
var i = names.Count;
while (true) {
i--;
if (!s.TopLevels.TryGetValue(names[i], out var topLevelDecl)) {
return null;
} else if (i == 0) {
// For reference-type declarations from other modules, we're picking up the NonNullTypeDecl; if so, return
// the original declaration.
return topLevelDecl is NonNullTypeDecl nntd ? nntd.ViewAsClass : topLevelDecl;
} else if (topLevelDecl is ModuleDecl moduleDecl) {
var signature = moduleDecl.AccessibleSignature(false);
s = resolver.GetSignature(signature);
} else {
return null;
}
}
}

bool InheritsFromObject(TraitDecl traitDecl) {
if (traitsProgress.TryGetValue(traitDecl, out var isDone)) {
if (isDone) {
return traitDecl.IsReferenceTypeDecl;
} else {
// there is a cycle among the parents, so we'll suppose the trait does inherit from "object"
return true;
}
}
traitsProgress[traitDecl] = false; // indicate that traitDecl is currently being visited

var inheritsFromObject = traitDecl.IsObjectTrait;
foreach (var parent in traitDecl.ParentTraits) {
if (parent is UserDefinedType udt) {
if (ResolveNamePath(udt.NamePath) is TraitDecl parentTrait) {
if (parentTrait.EnclosingModuleDefinition == this) {
inheritsFromObject = InheritsFromObject(parentTrait) || inheritsFromObject;
} else {
inheritsFromObject = parentTrait.IsReferenceTypeDecl || inheritsFromObject;
}
}
}
}

traitDecl.SetUpAsReferenceType(resolver.Options.Get(CommonOptionBag.GeneralTraits) ? inheritsFromObject : true);
traitsProgress[traitDecl] = true; // indicate that traitDecl.IsReferenceTypeDecl can now be called
return inheritsFromObject;
}

InheritsFromObject((TraitDecl)decl);
}
}
}
5 changes: 4 additions & 1 deletion Source/DafnyCore/AST/Modules/ScopeCloner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ public override TopLevelDecl CloneDeclaration(TopLevelDecl d, ModuleDefinition n
var tps = d.TypeArgs.ConvertAll(CloneTypeParam);
var characteristics = TypeParameter.GetExplicitCharacteristics(d);
var members = based is TopLevelDeclWithMembers tm ? tm.Members : new List<MemberDecl>();
var otd = new AbstractTypeDecl(Range(d.RangeToken), d.NameNode.Clone(this), newParent, characteristics, tps, members, CloneAttributes(d.Attributes), d.IsRefining);
// copy the newParent traits only if "d" is already an AbstractTypeDecl and is being export-revealed
var otd = new AbstractTypeDecl(Range(d.RangeToken), d.NameNode.Clone(this), newParent, characteristics, tps,
new List<Type>(), // omit the newParent traits
members, CloneAttributes(d.Attributes), d.IsRefining);
based = otd;
if (d is ClassLikeDecl { IsReferenceTypeDecl: true } cl) {
reverseMap.Add(based, cl.NonNullTypeDecl);
Expand Down
5 changes: 3 additions & 2 deletions Source/DafnyCore/AST/TypeDeclarations/AbstractTypeDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ public bool SupportsEquality {
get { return Characteristics.EqualitySupport != TypeParameter.EqualitySupportValue.Unspecified; }
}

public AbstractTypeDecl(RangeToken rangeToken, Name name, ModuleDefinition module, TypeParameter.TypeParameterCharacteristics characteristics, List<TypeParameter> typeArgs, List<MemberDecl> members, Attributes attributes, bool isRefining)
: base(rangeToken, name, module, typeArgs, members, attributes, isRefining) {
public AbstractTypeDecl(RangeToken rangeToken, Name name, ModuleDefinition module, TypeParameter.TypeParameterCharacteristics characteristics,
List<TypeParameter> typeArgs, List<Type> parentTraits, List<MemberDecl> members, Attributes attributes, bool isRefining)
: base(rangeToken, name, module, typeArgs, members, attributes, isRefining, parentTraits) {
Contract.Requires(rangeToken != null);
Contract.Requires(name != null);
Contract.Requires(module != null);
Expand Down
1 change: 0 additions & 1 deletion Source/DafnyCore/AST/TypeDeclarations/ClassDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ public class ClassDecl : ClassLikeDecl, IHasDocstring {
public override bool IsReferenceTypeDecl => true;
public override bool AcceptThis => true;

public override bool CanBeRevealed() { return true; }
[FilledInDuringResolution] public bool HasConstructor; // filled in (early) during resolution; true iff there exists a member that is a Constructor
[ContractInvariantMethod]
void ObjectInvariant() {
Expand Down
Loading