From a719fb8bf24459716704e5439e923371b34b6dfe Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Wed, 20 Aug 2025 11:26:15 -0400 Subject: [PATCH 1/8] Attempt at fixing issue 287 --- share/prolog/oorules/rules.pl | 49 ++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index 4f0710dc..1b303026 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2366,6 +2366,11 @@ reasonClassAtOffset(OuterClass, Offset, InnerClass), iso_dif(OuterClass, InnerClass), iso_dif(InnerClass, InnerMethod), + + % Ensure that InnerClass is only reachable through inheritance + forall(reasonClassAtOffset(OuterClass, Offset, InnerClass, Seq), + sequenceAreAllDerived(Seq)), + % Debugging logtraceln('~@~Q.', [not(factClassCallsMethod(InnerClass, InnerMethod)), reasonClassRelatedMethod_C(OuterClass, OuterMethod, @@ -2428,16 +2433,48 @@ % This predicate is used to see if the object at the listed offset is a Class, and if so, which % one. It's notable in that it is recursive however, so it can cover multiple levels. It % turns out this is important because inlining may hide one level of the hierarchy. -:- table reasonClassAtOffset/3 as incremental. -reasonClassAtOffset(OuterClass, Offset, InnerClass) :- - factObjectInObject(OuterClass, InnerClass, Offset). +:- table reasonClassAtOffset/4 as incremental. -reasonClassAtOffset(OuterClass, Offset, InnerClass) :- +%% reasonClassAtOffset(OuterClass, Offset, InnerClass, [Fact]) :- +%% Fact=factDerivedClass(OuterClass, InnerClass, Offset), +%% Fact. + +%% reasonClassAtOffset(OuterClass, Offset, InnerClass, [Fact]) :- +%% Fact=factEmbeddedObject(OuterClass, InnerClass, Offset), +%% Fact. + +reasonClassAtOffset_int(OuterClass, Offset, InnerClass, [Fact]) :- + Fact=factObjectInObject(OuterClass, InnerClass, Offset), + Fact. + +reasonClassAtOffset_int(OuterClass, Offset, InnerClass, L) :- ground(Offset), - reasonClassAtOffset(OuterClass, MiddleOffset, MiddleClass), + reasonClassAtOffset_int(OuterClass, MiddleOffset, MiddleClass, OL), % If Offset is bound, use it to bind InnerOffset. InnerOffset is Offset - MiddleOffset, - reasonClassAtOffset(MiddleClass, InnerOffset, InnerClass). + reasonClassAtOffset_int(MiddleClass, InnerOffset, InnerClass, IL), + append(OL, IL, L). + +refineHelper(factObjectInObject(OC, IC, Off), _) :- + (var(OC); var(IC); var(Off)), + throw(system_error(refineHelper)). + +refineHelper(factObjectInObject(OC, IC, Off), factDerivedClass(OC, IC, Off)) :- + factDerivedClass(OC, IC, Off). + +refineHelper(factObjectInObject(OC, IC, Off), factEmbeddedObject(OC, IC, Off)) :- + factEmbeddedObject(OC, IC, Off). + +reasonClassAtOffset(OuterClass, Offset, InnerClass, RefinedList) :- + reasonClassAtOffset_int(OuterClass, Offset, InnerClass, FactList), + maplist(refineHelper, FactList, RefinedList). + +reasonClassAtOffset(OuterClass, Offset, InnerClass) :- + reasonClassAtOffset_int(OuterClass, Offset, InnerClass, _). + +isDerivedHelper(factDerivedClass(_, _, _)). + +sequenceAreAllDerived(L) :- maplist(isDerivedHelper, L). % ejs 6/14/22 Isn't this just a more specific version of _C? It's not clear what the OIO tells % us. From e38f82d3d883c9473399b34be1152ed1fab2a1db Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Thu, 21 Aug 2025 09:15:44 -0400 Subject: [PATCH 2/8] Add additional debugging --- share/prolog/oorules/rules.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index 1b303026..692ac5d1 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2363,18 +2363,18 @@ find(OuterMethod, OuterClass), % We must know that there's an object within an object at that offset. % See comments in reasonClassCallsMethod_D about using reasonClassAtOffset... - reasonClassAtOffset(OuterClass, Offset, InnerClass), + reasonClassAtOffset(OuterClass, Offset, InnerClass, Seq), iso_dif(OuterClass, InnerClass), iso_dif(InnerClass, InnerMethod), % Ensure that InnerClass is only reachable through inheritance - forall(reasonClassAtOffset(OuterClass, Offset, InnerClass, Seq), - sequenceAreAllDerived(Seq)), + forall(reasonClassAtOffset(OuterClass, Offset, InnerClass2, Seq), + ((OuterClass=0x9ef0fc -> logtraceln('DEBUG ~Q ~Q', [InnerClass2, Seq]); true), sequenceAreAllDerived(Seq))), % Debugging logtraceln('~@~Q.', [not(factClassCallsMethod(InnerClass, InnerMethod)), reasonClassRelatedMethod_C(OuterClass, OuterMethod, - InnerClass, InnerMethod)]). + InnerClass, InnerMethod, offset=Offset, seq=Seq)]). % classCallsMethod(Class, Method) means that Method can be called by a method on Class. The From 917384fbfbe92733b6b49cfb3bc12b24f3174964 Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Thu, 21 Aug 2025 09:24:01 -0400 Subject: [PATCH 3/8] Don't fail when refinement is not possible --- share/prolog/oorules/rules.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index 692ac5d1..c5548004 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2465,6 +2465,8 @@ refineHelper(factObjectInObject(OC, IC, Off), factEmbeddedObject(OC, IC, Off)) :- factEmbeddedObject(OC, IC, Off). +refineHelper(factObjectInObject(OC, IC, Off), factObjectInObject(OC, IC, Off)). + reasonClassAtOffset(OuterClass, Offset, InnerClass, RefinedList) :- reasonClassAtOffset_int(OuterClass, Offset, InnerClass, FactList), maplist(refineHelper, FactList, RefinedList). From 2cf2902f1f31f2e1f6b82df4cd991a239b065b11 Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Fri, 22 Aug 2025 07:43:43 -0400 Subject: [PATCH 4/8] Fix/refine ClassRelatedMethod_C --- share/prolog/oorules/rules.pl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index c5548004..f166f745 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2368,13 +2368,19 @@ iso_dif(InnerClass, InnerMethod), % Ensure that InnerClass is only reachable through inheritance - forall(reasonClassAtOffset(OuterClass, Offset, InnerClass2, Seq), - ((OuterClass=0x9ef0fc -> logtraceln('DEBUG ~Q ~Q', [InnerClass2, Seq]); true), sequenceAreAllDerived(Seq))), + sequenceAreAllDerived(Seq), + + % And that *any* class at the same offset is only reachable through inheritance. + % Otherwise, we might be seeing a call to [Inherit, Embedded] but mistakenly say + % that InnerMethod, which is Embedded, is related. + forall(reasonClassAtOffset(OuterClass, Offset, _AnyInnerClassAtOffset, AnySeq), + sequenceAreAllDerived(AnySeq)), % Debugging logtraceln('~@~Q.', [not(factClassCallsMethod(InnerClass, InnerMethod)), reasonClassRelatedMethod_C(OuterClass, OuterMethod, - InnerClass, InnerMethod, offset=Offset, seq=Seq)]). + InnerClass, InnerMethod, + offset=Offset, seq=Seq)]). % classCallsMethod(Class, Method) means that Method can be called by a method on Class. The From b95bd06b10ff5c506de52739dcc0dee68f691869 Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Fri, 22 Aug 2025 16:26:20 -0400 Subject: [PATCH 5/8] Fix log --- share/prolog/oorules/rules.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index f166f745..007a11ce 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2377,7 +2377,7 @@ sequenceAreAllDerived(AnySeq)), % Debugging - logtraceln('~@~Q.', [not(factClassCallsMethod(InnerClass, InnerMethod)), + logtraceln('~@~Q.', [not(factClassRelatedMethod(InnerClass, InnerMethod)), reasonClassRelatedMethod_C(OuterClass, OuterMethod, InnerClass, InnerMethod, offset=Offset, seq=Seq)]). From 670a497c8a2c4b8e746b23216061236feb78f065 Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Mon, 25 Aug 2025 09:18:20 -0400 Subject: [PATCH 6/8] Add cuts --- share/prolog/oorules/rules.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index 007a11ce..ac10533c 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2466,10 +2466,10 @@ throw(system_error(refineHelper)). refineHelper(factObjectInObject(OC, IC, Off), factDerivedClass(OC, IC, Off)) :- - factDerivedClass(OC, IC, Off). + factDerivedClass(OC, IC, Off), !. refineHelper(factObjectInObject(OC, IC, Off), factEmbeddedObject(OC, IC, Off)) :- - factEmbeddedObject(OC, IC, Off). + factEmbeddedObject(OC, IC, Off), !. refineHelper(factObjectInObject(OC, IC, Off), factObjectInObject(OC, IC, Off)). From 3c70d3a970bf4f89cba7e590bd6f2c92fe2936f9 Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Mon, 25 Aug 2025 13:29:26 -0400 Subject: [PATCH 7/8] Try again --- share/prolog/oorules/rules.pl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index ac10533c..48882b36 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2367,14 +2367,9 @@ iso_dif(OuterClass, InnerClass), iso_dif(InnerClass, InnerMethod), - % Ensure that InnerClass is only reachable through inheritance - sequenceAreAllDerived(Seq), - - % And that *any* class at the same offset is only reachable through inheritance. - % Otherwise, we might be seeing a call to [Inherit, Embedded] but mistakenly say - % that InnerMethod, which is Embedded, is related. + % Ensure that any class at Offset is related (through inheritance). forall(reasonClassAtOffset(OuterClass, Offset, _AnyInnerClassAtOffset, AnySeq), - sequenceAreAllDerived(AnySeq)), + isOffsetPrecise(AnySeq)), % Debugging logtraceln('~@~Q.', [not(factClassRelatedMethod(InnerClass, InnerMethod)), @@ -2484,6 +2479,21 @@ sequenceAreAllDerived(L) :- maplist(isDerivedHelper, L). +longest_suffix(Pred, List, Suffix) :- + append(_, Suffix, List), % generates suffixes longest → shortest + call(maplist(Pred), Suffix), + !. + +zeroOff(factDerivedClass(_,_,0)). +zeroOff(factEmbeddedClass(_,_,0)). +zeroOff(factObjectInObject(_,_,0)). + +% Given a sub-object sequence, make sure that all objects at the final offset are derived, +% which ensures they are all related. +isOffsetPrecise(Seq) :- + longest_suffix(zeroOff, Seq, Suf), + sequenceAreAllDerived(Suf). + % ejs 6/14/22 Isn't this just a more specific version of _C? It's not clear what the OIO tells % us. From 4ae17ff6ebb3ac6ca16e6da019ead78e5b2b017e Mon Sep 17 00:00:00 2001 From: "Edward J. Schwartz" Date: Mon, 25 Aug 2025 13:36:45 -0400 Subject: [PATCH 8/8] refactor --- share/prolog/oorules/rules.pl | 5 ----- share/prolog/oorules/util.pl | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/share/prolog/oorules/rules.pl b/share/prolog/oorules/rules.pl index 48882b36..d39e8c8a 100644 --- a/share/prolog/oorules/rules.pl +++ b/share/prolog/oorules/rules.pl @@ -2479,11 +2479,6 @@ sequenceAreAllDerived(L) :- maplist(isDerivedHelper, L). -longest_suffix(Pred, List, Suffix) :- - append(_, Suffix, List), % generates suffixes longest → shortest - call(maplist(Pred), Suffix), - !. - zeroOff(factDerivedClass(_,_,0)). zeroOff(factEmbeddedClass(_,_,0)). zeroOff(factObjectInObject(_,_,0)). diff --git a/share/prolog/oorules/util.pl b/share/prolog/oorules/util.pl index e77fda83..3c302628 100644 --- a/share/prolog/oorules/util.pl +++ b/share/prolog/oorules/util.pl @@ -5,6 +5,11 @@ :- use_module(library(lists), [append/3, nth1/4, list_to_set/2]). +longest_suffix(Pred, List, Suffix) :- + append(_, Suffix, List), % generates suffixes longest → shortest + call(maplist(Pred), Suffix), + !. + sort_tuple((A,B), (C,D)) :- (A < B -> (C=A, D=B); (C=B, D=A)).