| 1390 | } |
| 1391 | |
| 1392 | func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { |
| 1393 | var diags hcl.Diagnostics |
| 1394 | var marks []cty.ValueMarks |
| 1395 | |
| 1396 | collVal, collDiags := e.CollExpr.Value(ctx) |
| 1397 | diags = append(diags, collDiags...) |
| 1398 | |
| 1399 | if collVal.IsNull() { |
| 1400 | diags = append(diags, &hcl.Diagnostic{ |
| 1401 | Severity: hcl.DiagError, |
| 1402 | Summary: "Iteration over null value", |
| 1403 | Detail: "A null value cannot be used as the collection in a 'for' expression.", |
| 1404 | Subject: e.CollExpr.Range().Ptr(), |
| 1405 | Context: &e.SrcRange, |
| 1406 | Expression: e.CollExpr, |
| 1407 | EvalContext: ctx, |
| 1408 | }) |
| 1409 | return cty.DynamicVal, diags |
| 1410 | } |
| 1411 | if collVal.Type() == cty.DynamicPseudoType { |
| 1412 | return cty.DynamicVal, diags |
| 1413 | } |
| 1414 | // Unmark collection before checking for iterability, because marked |
| 1415 | // values cannot be iterated |
| 1416 | collVal, collMarks := collVal.Unmark() |
| 1417 | marks = append(marks, collMarks) |
| 1418 | if !collVal.CanIterateElements() { |
| 1419 | diags = append(diags, &hcl.Diagnostic{ |
| 1420 | Severity: hcl.DiagError, |
| 1421 | Summary: "Iteration over non-iterable value", |
| 1422 | Detail: fmt.Sprintf( |
| 1423 | "A value of type %s cannot be used as the collection in a 'for' expression.", |
| 1424 | collVal.Type().FriendlyName(), |
| 1425 | ), |
| 1426 | Subject: e.CollExpr.Range().Ptr(), |
| 1427 | Context: &e.SrcRange, |
| 1428 | Expression: e.CollExpr, |
| 1429 | EvalContext: ctx, |
| 1430 | }) |
| 1431 | return cty.DynamicVal, diags |
| 1432 | } |
| 1433 | |
| 1434 | // Grab the CondExpr marks when we're returning early with an unknown |
| 1435 | var condMarks cty.ValueMarks |
| 1436 | |
| 1437 | // Before we start we'll do an early check to see if any CondExpr we've |
| 1438 | // been given is of the wrong type. This isn't 100% reliable (it may |
| 1439 | // be DynamicVal until real values are given) but it should catch some |
| 1440 | // straightforward cases and prevent a barrage of repeated errors. |
| 1441 | if e.CondExpr != nil { |
| 1442 | childCtx := ctx.NewChild() |
| 1443 | childCtx.Variables = map[string]cty.Value{} |
| 1444 | if e.KeyVar != "" { |
| 1445 | childCtx.Variables[e.KeyVar] = cty.DynamicVal |
| 1446 | } |
| 1447 | childCtx.Variables[e.ValVar] = cty.DynamicVal |
| 1448 | |
| 1449 | result, condDiags := e.CondExpr.Value(childCtx) |