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

Add NeedsGuard() in consumers #82

Merged
merged 19 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 64 additions & 22 deletions annotation/consume_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ type ConsumingAnnotationTrigger interface {
// This implies loss of information about the assignment. This method is used to track such assignments and print
// a more informative error message.
AddAssignment(Assignment)
}

// customPos has the below default implementations, in which case ConsumeTrigger.Pos() will return a default value.
// To return non-default position values, this method should be overridden appropriately.
func (t *TriggerIfNonNil) customPos() (token.Pos, bool) { return 0, false }
func (t *TriggerIfDeepNonNil) customPos() (token.Pos, bool) { return 0, false }
func (t *ConsumeTriggerTautology) customPos() (token.Pos, bool) { return 0, false }
// NeedsGuard returns true if the trigger needs to be guarded, for example, by a nil check or an ok form.
NeedsGuard() bool

// SetNeedsGuard sets the underlying Guard-Neediness of this ConsumerTrigger, if present.
// Default setting for ConsumerTriggers is that they need a guard. Override this method to set the need for a guard to false.
SetNeedsGuard(bool)
}

// Prestring is an interface used to encode objects that have compact on-the-wire encodings
// (via gob) but can still be expanded into verbose string representations on demand using
Expand Down Expand Up @@ -152,12 +153,13 @@ func (a *assignmentFlow) String() string {

// TriggerIfNonNil is triggered if the contained Annotation is non-nil
type TriggerIfNonNil struct {
Ann Key
Ann Key
IsGuardNotNeeded bool // ConsumeTriggers need guards by default, when applicable. Set this to true when guards are not needed.
assignmentFlow
}

// Kind returns Conditional.
func (t *TriggerIfNonNil) Kind() TriggerKind { return Conditional }
func (*TriggerIfNonNil) Kind() TriggerKind { return Conditional }

// UnderlyingSite the underlying site this trigger's nilability depends on.
func (t *TriggerIfNonNil) UnderlyingSite() Key { return t.Ann }
Expand All @@ -168,10 +170,22 @@ func (t *TriggerIfNonNil) CheckConsume(annMap Map) bool {
return ok && !ann.IsNilable
}

// customPos has the below default implementation for TriggerIfNonNil, in which case ConsumeTrigger.Pos() will return a default value.
// To return non-default position values, this method should be overridden appropriately.
func (*TriggerIfNonNil) customPos() (token.Pos, bool) { return token.NoPos, false }

// NeedsGuard is the default implementation for TriggerIfNonNil. To return non-default value, this method should be overridden.
func (t *TriggerIfNonNil) NeedsGuard() bool { return !t.IsGuardNotNeeded }

// SetNeedsGuard sets the underlying Guard-Neediness of this ConsumerTrigger
func (t *TriggerIfNonNil) SetNeedsGuard(b bool) {
t.IsGuardNotNeeded = !b
}

// equals returns true if the passed ConsumingAnnotationTrigger is equal to this one
func (t *TriggerIfNonNil) equals(other ConsumingAnnotationTrigger) bool {
if other, ok := other.(*TriggerIfNonNil); ok {
return t.Ann.equals(other.Ann)
return t.Ann.equals(other.Ann) && t.IsGuardNotNeeded == other.IsGuardNotNeeded
}
return false
}
Expand Down Expand Up @@ -210,12 +224,13 @@ func (t TriggerIfNonNilPrestring) String() string {

// TriggerIfDeepNonNil is triggered if the contained Annotation is deeply non-nil
type TriggerIfDeepNonNil struct {
Ann Key
Ann Key
IsGuardNotNeeded bool // ConsumeTriggers need guards by default, when applicable. Set this to true when guards are not needed.
assignmentFlow
}

// Kind returns DeepConditional.
func (t *TriggerIfDeepNonNil) Kind() TriggerKind { return DeepConditional }
func (*TriggerIfDeepNonNil) Kind() TriggerKind { return DeepConditional }

// UnderlyingSite the underlying site this trigger's nilability depends on.
func (t *TriggerIfDeepNonNil) UnderlyingSite() Key { return t.Ann }
Expand All @@ -226,10 +241,22 @@ func (t *TriggerIfDeepNonNil) CheckConsume(annMap Map) bool {
return ok && !ann.IsDeepNilable
}

// customPos has the below default implementation for TriggerIfDeepNonNil, in which case ConsumeTrigger.Pos() will return a default value.
// To return non-default position values, this method should be overridden appropriately.
func (*TriggerIfDeepNonNil) customPos() (token.Pos, bool) { return token.NoPos, false }

// NeedsGuard default implementation for TriggerIfDeepNonNil. To return non-default value, this method should be overridden.
func (t *TriggerIfDeepNonNil) NeedsGuard() bool { return !t.IsGuardNotNeeded }

// SetNeedsGuard sets the underlying Guard-Neediness of this ConsumerTrigger
func (t *TriggerIfDeepNonNil) SetNeedsGuard(b bool) {
t.IsGuardNotNeeded = !b
}

// equals returns true if the passed ConsumingAnnotationTrigger is equal to this one
func (t *TriggerIfDeepNonNil) equals(other ConsumingAnnotationTrigger) bool {
if other, ok := other.(*TriggerIfDeepNonNil); ok {
return t.Ann.equals(other.Ann)
return t.Ann.equals(other.Ann) && t.IsGuardNotNeeded == other.IsGuardNotNeeded
}
return false
}
Expand Down Expand Up @@ -268,6 +295,7 @@ func (t TriggerIfDeepNonNilPrestring) String() string {

// ConsumeTriggerTautology is used at consumption sites were consuming nil is always an error
type ConsumeTriggerTautology struct {
IsGuardNotNeeded bool // ConsumeTriggers need guards by default, when applicable. Set this to true when guards are not needed.
assignmentFlow
}

Expand All @@ -280,28 +308,42 @@ func (*ConsumeTriggerTautology) UnderlyingSite() Key { return nil }
// CheckConsume returns true
func (*ConsumeTriggerTautology) CheckConsume(Map) bool { return true }

// customPos has the below default implementation for ConsumeTriggerTautology, in which case ConsumeTrigger.Pos() will return a default value.
// To return non-default position values, this method should be overridden appropriately.
func (*ConsumeTriggerTautology) customPos() (token.Pos, bool) { return token.NoPos, false }

// NeedsGuard default implementation for ConsumeTriggerTautology. To return non-default value, this method should be overridden.
func (c *ConsumeTriggerTautology) NeedsGuard() bool { return !c.IsGuardNotNeeded }

// SetNeedsGuard sets the underlying Guard-Neediness of this ConsumerTrigger
func (c *ConsumeTriggerTautology) SetNeedsGuard(b bool) {
c.IsGuardNotNeeded = !b
}

// equals returns true if the passed ConsumingAnnotationTrigger is equal to this one
func (*ConsumeTriggerTautology) equals(other ConsumingAnnotationTrigger) bool {
_, ok := other.(*ConsumeTriggerTautology)
return ok
func (c *ConsumeTriggerTautology) equals(other ConsumingAnnotationTrigger) bool {
if other, ok := other.(*ConsumeTriggerTautology); ok {
return c.IsGuardNotNeeded == other.IsGuardNotNeeded
}
return false
}

// Copy returns a deep copy of this ConsumingAnnotationTrigger
func (t *ConsumeTriggerTautology) Copy() ConsumingAnnotationTrigger {
copyConsumer := *t
copyConsumer.assignmentFlow = t.assignmentFlow.copy()
func (c *ConsumeTriggerTautology) Copy() ConsumingAnnotationTrigger {
copyConsumer := *c
copyConsumer.assignmentFlow = c.assignmentFlow.copy()
return &copyConsumer
}

// AddAssignment adds an assignment to the trigger.
func (t *ConsumeTriggerTautology) AddAssignment(e Assignment) {
t.assignmentFlow.addEntry(e)
func (c *ConsumeTriggerTautology) AddAssignment(e Assignment) {
c.assignmentFlow.addEntry(e)
}

// Prestring returns this Prestring as a Prestring
func (t *ConsumeTriggerTautology) Prestring() Prestring {
func (c *ConsumeTriggerTautology) Prestring() Prestring {
return ConsumeTriggerTautologyPrestring{
AssignmentStr: t.assignmentFlow.String(),
AssignmentStr: c.assignmentFlow.String(),
}
}

Expand Down
3 changes: 2 additions & 1 deletion annotation/produce_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ type ProducingAnnotationTrigger interface {
NeedsGuardMatch() bool

// SetNeedsGuard sets the underlying Guard-Neediness of this ProduceTrigger, if present
// This should be very sparingly used, and only with utter conviction of correctness
// This should be very sparingly used, and only with utter conviction of correctness.
// Default setting for ProduceTriggers is to not need a guard.
SetNeedsGuard(bool)

Prestring() Prestring
Expand Down
2 changes: 1 addition & 1 deletion assertion/function/assertiontree/backprop_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ func exprAsDeepProducer(rootNode *RootAssertionNode, expr ast.Expr) annotation.P
// trigger is ignored and replaced with an always-nilable-producing
// instance of annotation.GuardMissing
func CheckGuardOnFullTrigger(trigger annotation.FullTrigger) annotation.FullTrigger {
if trigger.Producer.Annotation.NeedsGuardMatch() && !trigger.Consumer.GuardMatched {
if trigger.Producer.Annotation.NeedsGuardMatch() && trigger.Consumer.Annotation.NeedsGuard() && !trigger.Consumer.GuardMatched {
return annotation.FullTrigger{
Producer: &annotation.ProduceTrigger{
Annotation: &annotation.GuardMissing{
Expand Down
Loading