MCPcopy
hub / github.com/uber-go/nilaway / BackpropAcrossFunc

Function BackpropAcrossFunc

assertion/function/assertiontree/backprop.go:901–1082  ·  view source on GitHub ↗

BackpropAcrossFunc is the main driver of the backpropagation, it takes a function declaration with accompanying CFG, and back-propagates a tree of assertions across it to generate, at entry to the function, the set of assertions that must hold to avoid possible nil flow errors.

(
	ctx context.Context,
	pass *analysishelper.EnhancedPass,
	decl *ast.FuncDecl,
	functionContext FunctionContext,
	graph *cfg.CFG,
)

Source from the content-addressed store, hash-verified

899// with accompanying CFG, and back-propagates a tree of assertions across it to generate, at entry
900// to the function, the set of assertions that must hold to avoid possible nil flow errors.
901func BackpropAcrossFunc(
902 ctx context.Context,
903 pass *analysishelper.EnhancedPass,
904 decl *ast.FuncDecl,
905 functionContext FunctionContext,
906 graph *cfg.CFG,
907) ([]annotation.FullTrigger, int, int, error) {
908 // We transform the CFG to have it reflect the implicit control flow that happens
909 // inside short-circuiting boolean expressions.
910 preprocessor := preprocess.New(pass)
911 graph = preprocessor.CFG(graph, functionContext.funcDecl)
912
913 // Generate rick check effects.
914 richCheckBlocks, exprNonceMap := genInitialRichCheckEffects(graph, functionContext)
915 richCheckBlocks = propagateRichChecks(graph, richCheckBlocks)
916 blocks, preprocessing := blocksAndPreprocessingFromCFG(pass, graph, richCheckBlocks)
917
918 // The assertion nodes for each block and an array of bools to indicate whether each block is
919 // updated in this round or not.
920 // DANGER: anytime a pointer is copied from currAssertions to nextAssertions, it MUST be
921 // treated as immutable - if any modification (e.g. merging or backprop) is going to occur you
922 // must perform a deep copy with CopyNode
923 updatedLastRound, updatedThisRound := make([]bool, len(blocks)), make([]bool, len(blocks))
924 currAssertions, nextAssertions := make([]*RootAssertionNode, len(blocks)), make([]*RootAssertionNode, len(blocks))
925
926 // The assertion nodes for the entry block, we will use it as an indication for stabilization.
927 // We consider the backpropagation stable if # of stable rounds > # of live blocks + tolerance.
928 var currRootAssertionNode, nextRootAssertionNode *RootAssertionNode
929 roundCount, stableRoundCount := 0, 0
930 postOrder := computePostOrder(blocks)
931
932 // Initialize the process by creating the assertion nodes for the return block.
933 retBlock := len(blocks) - 1
934 currAssertions[retBlock] = newRootAssertionNode(exprNonceMap, functionContext)
935 updatedLastRound[retBlock] = true
936
937 for slices.Contains(updatedLastRound, true) {
938 roundCount++
939
940 select {
941 case <-ctx.Done():
942 return nil, roundCount, stableRoundCount, fmt.Errorf("backprop early stop due to context: %w", ctx.Err())
943 default:
944 }
945
946 for _, i := range postOrder {
947 block := blocks[i]
948
949 // There is no need to process non-live blocks; their assertions will simply be nil.
950 if !block.Live {
951 continue
952 }
953
954 if len(block.Succs) > 2 {
955 return nil, roundCount, stableRoundCount, errors.New("assumptions about CFG shape violated - a block has >2 successors")
956 }
957
958 // No need to re-process the assertion node for the current block if it does not have

Callers 2

analyzeFuncFunction · 0.92

Calls 15

ProcessEntryMethod · 0.95
NewFunction · 0.92
FullTriggerSlicesEqFunction · 0.92
propagateRichChecksFunction · 0.85
computePostOrderFunction · 0.85
newRootAssertionNodeFunction · 0.85
CopyNodeFunction · 0.85
backpropAcrossBlockFunction · 0.85
CFGMethod · 0.80

Tested by 1