@@ -1732,17 +1732,37 @@ ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImport
17321732 }
17331733 // add deconstructors now (that is, after the query methods have been added)
17341734 foreach (DatatypeCtor ctor in dt.Ctors) {
1735+ var formalsUsedInThisCtor = new HashSet<string>();
17351736 foreach (var formal in ctor.Formals) {
1736- bool nameError = false;
1737- if (formal.HasName && members.ContainsKey(formal.Name)) {
1738- reporter.Error(MessageSource.Resolver, ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
1739- nameError = true;
1737+ MemberDecl previousMember = null;
1738+ var localDuplicate = false;
1739+ if (formal.HasName) {
1740+ if (members.TryGetValue(formal.Name, out previousMember)) {
1741+ localDuplicate = formalsUsedInThisCtor.Contains(formal.Name);
1742+ if (localDuplicate) {
1743+ reporter.Error(MessageSource.Resolver, ctor, "Duplicate use of deconstructor name in the same constructor: {0}", formal.Name);
1744+ } else if (previousMember is DatatypeDestructor) {
1745+ // this is okay, if the destructor has the appropriate type; this will be checked later, after type checking
1746+ } else {
1747+ reporter.Error(MessageSource.Resolver, ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name);
1748+ }
1749+ }
1750+ formalsUsedInThisCtor.Add(formal.Name);
17401751 }
1741- var dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.CompileName, "", "", formal.IsGhost, formal.Type, null);
1742- dtor.InheritVisibility(dt);
1743- dtor.EnclosingClass = dt; // resolve here
1744- if (!nameError && formal.HasName) {
1745- members.Add(formal.Name, dtor);
1752+ DatatypeDestructor dtor;
1753+ if (!localDuplicate && previousMember is DatatypeDestructor) {
1754+ // a destructor with this name already existed in (a different constructor in) the datatype
1755+ dtor = (DatatypeDestructor)previousMember;
1756+ dtor.AddAnotherEnclosingCtor(ctor, formal);
1757+ } else {
1758+ // either the destructor has no explicit name, or this constructor declared another destructor with this name, or no previous destructor had this name
1759+ dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.CompileName, "", "", formal.IsGhost, formal.Type, null);
1760+ dtor.InheritVisibility(dt);
1761+ dtor.EnclosingClass = dt; // resolve here
1762+ if (formal.HasName && !localDuplicate && previousMember == null) {
1763+ // the destructor has an explict name and there was no member at all with this name before
1764+ members.Add(formal.Name, dtor);
1765+ }
17461766 }
17471767 ctor.Destructors.Add(dtor);
17481768 }
@@ -2119,6 +2139,7 @@ public void ResolveTopLevelDecls_Core(List<TopLevelDecl/*!*/>/*!*/ declarations,
21192139 // ---------------------------------- Pass 1 ----------------------------------
21202140 // This pass:
21212141 // * checks that type inference was able to determine all types
2142+ // * check that shared destructors in datatypes are in agreement
21222143 // * fills in the .ResolvedOp field of binary expressions
21232144 // * discovers bounds for:
21242145 // - forall statements
@@ -2140,6 +2161,7 @@ public void ResolveTopLevelDecls_Core(List<TopLevelDecl/*!*/>/*!*/ declarations,
21402161 }
21412162 if (reporter.Count(ErrorLevel.Error) == prevErrorCount) {
21422163 // Check that type inference went well everywhere; this will also fill in the .ResolvedOp field in binary expressions
2164+ // Also, for each datatype, check that shared destructors are in agreement
21432165 foreach (TopLevelDecl d in declarations) {
21442166 if (d is IteratorDecl) {
21452167 var iter = (IteratorDecl)d;
@@ -2191,6 +2213,28 @@ public void ResolveTopLevelDecls_Core(List<TopLevelDecl/*!*/>/*!*/ declarations,
21912213 CheckExpression(dd.Constraint, this, dd);
21922214 }
21932215 FigureOutNativeType(dd, nativeTypeMap);
2216+ } else if (d is DatatypeDecl) {
2217+ var dd = (DatatypeDecl)d;
2218+ foreach (var member in datatypeMembers[dd].Values) {
2219+ var dtor = member as DatatypeDestructor;
2220+ if (dtor != null) {
2221+ var rolemodel = dtor.CorrespondingFormals[0];
2222+ for (int i = 1; i < dtor.CorrespondingFormals.Count; i++) {
2223+ var other = dtor.CorrespondingFormals[i];
2224+ if (!rolemodel.Type.ExactlyEquals(other.Type)) {
2225+ reporter.Error(MessageSource.Resolver, other,
2226+ "shared destructors must have the same type, but '{0}' has type '{1}' in constructor '{2}' and type '{3}' in constructor '{4}'",
2227+ rolemodel.Name, rolemodel.Type, dtor.EnclosingCtors[0].Name, other.Type, dtor.EnclosingCtors[i].Name);
2228+ } else if (rolemodel.IsGhost != other.IsGhost) {
2229+ reporter.Error(MessageSource.Resolver, other,
2230+ "shared destructors must agree on whether or not they are ghost, but '{0}' is {1} in constructor '{2}' and {3} in constructor '{4}'",
2231+ rolemodel.Name,
2232+ rolemodel.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[0].Name,
2233+ other.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[i].Name);
2234+ }
2235+ }
2236+ }
2237+ }
21942238 }
21952239 }
21962240 }
@@ -9885,7 +9929,7 @@ public void ResolveExpressionX(Expression expr, ResolveOpts opts) {
98859929 if (!ty.IsDatatype) {
98869930 reporter.Error(MessageSource.Resolver, expr, "datatype update expression requires a root expression of a datatype (got {0})", ty);
98879931 } else {
9888- var let = ResolveDatatypeUpdate(expr.tok, e.Root, ty.AsDatatype, e.Updates, false, opts);
9932+ var let = ResolveDatatypeUpdate(expr.tok, e.Root, ty.AsDatatype, e.Updates, opts);
98899933 if (let != null) {
98909934 e.ResolvedExpression = let;
98919935 expr.Type = ty;
@@ -10409,7 +10453,7 @@ private void AddXConstraint(IToken tok, string constraintName, Type type, Expres
1040910453 /// <summary>
1041010454 /// Attempts to produce a let expression from the given datatype updates. Returns that let expression upon success, and null otherwise.
1041110455 /// </summary>
10412- Expression ResolveDatatypeUpdate(IToken tok, Expression root, DatatypeDecl dt, List<Tuple<IToken, string, Expression>> memberUpdates, bool sequentialUpdates, ResolveOpts opts) {
10456+ Expression ResolveDatatypeUpdate(IToken tok, Expression root, DatatypeDecl dt, List<Tuple<IToken, string, Expression>> memberUpdates, ResolveOpts opts) {
1041310457 Contract.Requires(tok != null);
1041410458 Contract.Requires(root != null);
1041510459 Contract.Requires(dt != null);
@@ -10427,28 +10471,29 @@ Expression ResolveDatatypeUpdate(IToken tok, Expression root, DatatypeDecl dt, L
1042710471 foreach (var entry in memberUpdates) {
1042810472 var destructor_str = entry.Item2;
1042910473 if (memberNames.Contains(destructor_str)) {
10430- if (sequentialUpdates) {
10431- reporter.Warning(MessageSource.Resolver, entry.Item1, "update to '{0}' overwritten by another update to '{0}'", destructor_str);
10432- } else {
10433- reporter.Error(MessageSource.Resolver, entry.Item1, "duplicate update member '{0}'", destructor_str);
10434- }
10474+ reporter.Error(MessageSource.Resolver, entry.Item1, "duplicate update member '{0}'", destructor_str);
1043510475 } else {
1043610476 memberNames.Add(destructor_str);
1043710477 MemberDecl member;
1043810478 if (!datatypeMembers[dt].TryGetValue(destructor_str, out member)) {
1043910479 reporter.Error(MessageSource.Resolver, entry.Item1, "member '{0}' does not exist in datatype '{1}'", destructor_str, dt.Name);
10480+ } else if (!(member is DatatypeDestructor)) {
10481+ reporter.Error(MessageSource.Resolver, entry.Item1, "member '{0}' is not a destructor in datatype '{1}'", destructor_str, dt.Name);
1044010482 } else {
1044110483 var destructor = (DatatypeDestructor)member;
10442- if (ctor != null && ctor != destructor.EnclosingCtor) {
10443- if (sequentialUpdates) {
10444- // This would eventually give rise to a verification error. However, since the 'sequentialUpdates' case is being depricated,
10445- // we don't mind giving resolution error about this now.
10446- }
10484+ if (destructor.EnclosingCtors.Count > 1) {
10485+ // Note: This restriction could be relaxed. For example, thing would still be okay if the intersection of constructors of
10486+ // the indicated memberUpdates names indicate a unique constructor. In addition, the syntax could be extended to allow an
10487+ // update member to have the form ctor.x:=E where ctor would be used to disambiguate which constructor the x is supposed to
10488+ // be looked up in.
10489+ reporter.Error(MessageSource.Resolver, entry.Item1, "datatype member update is supported only for uniquely named destructors " +
10490+ "('{0}' belongs to '{1}')", entry.Item2, destructor.EnclosingCtorNames("and"));
10491+ } else if (ctor != null && ctor != destructor.EnclosingCtors[0]) {
1044710492 reporter.Error(MessageSource.Resolver, entry.Item1, "updated datatype members must belong to the same constructor " +
10448- "('{0}' belongs to '{1}' and '{2}' belongs to '{3}'", entry.Item2, destructor.EnclosingCtor .Name, ctorSource.Item2, ctor.Name);
10493+ "('{0}' belongs to '{1}' and '{2}' belongs to '{3}') ", entry.Item2, destructor.EnclosingCtors[0] .Name, ctorSource.Item2, ctor.Name);
1044910494 } else {
10450- updates.Add(destructor.CorrespondingFormal , entry.Item3);
10451- ctor = destructor.EnclosingCtor ;
10495+ updates.Add(destructor.CorrespondingFormals[0] , entry.Item3);
10496+ ctor = destructor.EnclosingCtors[0] ;
1045210497 ctorSource = entry;
1045310498 }
1045410499 }
0 commit comments